From db5fc05d0815db56aca8cad143f2f7895cdced54 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 18:55:25 +0700 Subject: [PATCH 01/86] Update bot activity message --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 36e588cd42..b584e63e0d 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Made with ā¤ļø", + name: "Join M00NKID Today!", // Activity type number (0 = Playing). type: 0, }, From ba37b04eee17b4695c90948d7415f320d0d6fdb8 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 18:59:54 +0700 Subject: [PATCH 02/86] Update bot activity message to include 'Team' --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index b584e63e0d..a81ce5a0f1 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Join M00NKID Today!", + name: "Join Team M00NKID Today!", // Activity type number (0 = Playing). type: 0, }, From c4fb2ec5691524df76e7852ec27176fc945a77f5 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 19:11:32 +0700 Subject: [PATCH 03/86] Change the bot's primary and secondary colors to white and black --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index a81ce5a0f1..6a2d1510d6 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -88,8 +88,8 @@ export const botConfig = { embeds: { colors: { // Main brand colors. - primary: "#336699", - secondary: "#2F3136", + primary: "#FFFFFF", + secondary: "#000000", // Standard status colors for success/error/warning/info messages. success: "#57F287", From 52caca7afdf5268d9cac91aa017b16bb6c6ebfde Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 19:22:26 +0700 Subject: [PATCH 04/86] Update activity name for bot configuration --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 6a2d1510d6..7e65e4e13e 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Join Team M00NKID Today!", + name: "Team M00NKID Join Today!", // Activity type number (0 = Playing). type: 0, }, From bcf7f4adf43dbf34d51c457ce977fc31042c7d43 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 19:24:30 +0700 Subject: [PATCH 05/86] Change bot activity type from Playing to Watching --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 7e65e4e13e..b8ff01cd7f 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -27,7 +27,7 @@ export const botConfig = { // Text users will see (example: "Playing /help | Titan Bot"). name: "Team M00NKID Join Today!", // Activity type number (0 = Playing). - type: 0, + type: 3, }, ], }, From 7e3b5fb7a12a6e7157af85d256e03e85d6d7b0fa Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 19:47:06 +0700 Subject: [PATCH 06/86] Update bot activity name to 'Team M00NKIDD Join Today!' --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index b8ff01cd7f..36d48e6938 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Team M00NKID Join Today!", + name: "Team M00NKIDD Join Today!", // Activity type number (0 = Playing). type: 3, }, From 69d31354f8cbb206c6ee0461269881a0672fd71b Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 19:48:34 +0700 Subject: [PATCH 07/86] Fix bot activity name typo --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 36d48e6938..6048547ca2 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Team M00NKIDD Join Today!", + name: "Team M00NK1DD Join Today!", // Activity type number (0 = Playing). type: 3, }, From 309163a44f9098d06bee0e53a2f49f875f1a0277 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 20:21:04 +0700 Subject: [PATCH 08/86] Update bot owners to specific user IDs --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 6048547ca2..1dcbd4e7d5 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -38,7 +38,7 @@ export const botConfig = { commands: { // Bot owner user IDs (comma-separated in OWNER_IDS env var). // Owners can access owner/admin-level bot commands. - owners: process.env.OWNER_IDS?.split(",") || [], + owners: process.env.OWNER_IDS?.split("1398076402460524545,1291778460641136641") || [], // Default wait time between command uses (in seconds). defaultCooldown: 3, From b58b88f6d6a8f9063f511ce7349033e95bb0b045 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 20:25:13 +0700 Subject: [PATCH 09/86] Update the bot owners' list in the configuration --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 1dcbd4e7d5..0ca68b9028 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -38,7 +38,7 @@ export const botConfig = { commands: { // Bot owner user IDs (comma-separated in OWNER_IDS env var). // Owners can access owner/admin-level bot commands. - owners: process.env.OWNER_IDS?.split("1398076402460524545,1291778460641136641") || [], + owners: process.env.OWNER_IDS?.split("1398076402460524545,1291778460641136641,1323824091601440800,1189326159285076129") || [], // Default wait time between command uses (in seconds). defaultCooldown: 3, From 35d6728160397a54c62392f6054c7f1d27562e1c Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 20:43:55 +0700 Subject: [PATCH 10/86] Update default application questions --- src/config/bot.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 0ca68b9028..8b10b6d9b9 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -56,9 +56,12 @@ export const botConfig = { applications: { // Default questions shown when someone fills out an application. defaultQuestions: [ - { question: "What is your name?", required: true }, + { question: "What is your Username? (NOT DISPLAY NAME)", required: true }, { question: "How old are you?", required: true }, { question: "Why do you want to join?", required: true }, + { question: "Do you have Skill In Coding?", required: true }, + { question: "if a person is spamming what would you do?", required: true }, + { question: "Any other thing you want to say?", required: true }, ], // Embed colors by application status. From 5d42876b226828a21b80334375ce2e883bf6c885 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 20:56:51 +0700 Subject: [PATCH 11/86] Update bot activity name to include 'Watching You' --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 8b10b6d9b9..1846260aa6 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Team M00NK1DD Join Today!", + name: "Watching You | Team M00NK1DD Join Today!", // Activity type number (0 = Playing). type: 3, }, From 51cfd8372105b6259b86bbb04e659ad2e6f9077a Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 20:58:40 +0700 Subject: [PATCH 12/86] Update bot activity name in bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 1846260aa6..cbf2752c30 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Watching You | Team M00NK1DD Join Today!", + name: "Watching Channels | Team M00NK1DD Join Today!", // Activity type number (0 = Playing). type: 3, }, From 595af9be225dece063f954cd8540b1ac2781a2a3 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 22:06:55 +0700 Subject: [PATCH 13/86] Update bot activity message with new text --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index cbf2752c30..6b35c28af6 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Watching Channels | Team M00NK1DD Join Today!", + name: "Watching Channels | Team M00NK1DD Join Today! M00NK1DD šŸ”›šŸ”", // Activity type number (0 = Playing). type: 3, }, From de0a35b35e959bb36c897742c42a4ca46ef5707c Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 7 Jun 2026 22:09:34 +0700 Subject: [PATCH 14/86] Update bot activity name for clarity --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 6b35c28af6..3d27c451b5 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Watching Channels | Team M00NK1DD Join Today! M00NK1DD šŸ”›šŸ”", + name: "Watching Channels | Team M00NK1DD Join Today! M00NK1DD > C00lkid ", // Activity type number (0 = Playing). type: 3, }, From d17ce18f13e12f81a919336f92cac76319177024 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 8 Jun 2026 14:01:48 +0700 Subject: [PATCH 15/86] Update bot.js activity name and type, add manager roles --- src/config/bot.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 3d27c451b5..7265f1d351 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,9 +25,9 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Watching Channels | Team M00NK1DD Join Today! M00NK1DD > C00lkid ", + name: "Watching Channels | Team M00NK1DD Join Today!", // Activity type number (0 = Playing). - type: 3, + type: 4, }, ], }, @@ -81,7 +81,7 @@ export const botConfig = { deleteApprovedAfter: 30, // Role IDs allowed to manage applications. - managerRoles: [], // Will be populated from environment or database + managerRoles: [1477697404592455927,1497297060318150696,1470022111916462247], // Will be populated from environment or database }, // ========================= From 1df521311fbd818c59b5226d098ca838bf183d50 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 8 Jun 2026 19:58:46 +0700 Subject: [PATCH 16/86] Change bot activity type from 4 to 3 --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 7265f1d351..f5a23ed455 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -27,7 +27,7 @@ export const botConfig = { // Text users will see (example: "Playing /help | Titan Bot"). name: "Watching Channels | Team M00NK1DD Join Today!", // Activity type number (0 = Playing). - type: 4, + type: 3, }, ], }, From e30bb68b59e98fde8787dadb6236488e8d71da1b Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 8 Jun 2026 20:33:55 +0700 Subject: [PATCH 17/86] Change bot footer text from 'Titan Bot' to 'M00NB0T' --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index f5a23ed455..447e08746b 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -139,7 +139,7 @@ export const botConfig = { }, footer: { // Default footer text used in bot embeds. - text: "Titan Bot", + text: "M00NB0T", // Footer icon URL (null = no icon). icon: null, }, From 62c8816935b63f1a015fdbfb22a17f30f5e8ce03 Mon Sep 17 00:00:00 2001 From: arand606 Date: Thu, 11 Jun 2026 22:10:11 +0700 Subject: [PATCH 18/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 447e08746b..3fa8ebc13f 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -12,7 +12,7 @@ export const botConfig = { // - "invisible" = appears offline presence: { // Current online state shown on Discord. - status: "online", + status: "dnd", // Activity lines shown under the bot name. // `type` number mapping from Discord: From ed127a2b82a051fe3f92c26f7b25a6c636b3043e Mon Sep 17 00:00:00 2001 From: arand606 Date: Thu, 11 Jun 2026 22:14:03 +0700 Subject: [PATCH 19/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 3fa8ebc13f..3262b56521 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Watching Channels | Team M00NK1DD Join Today!", + name: "Watching Channels | Team M00NK1DD Join Today!(under maintenance.)", // Activity type number (0 = Playing). type: 3, }, From 44a47e75af821856b1be41125c804bffb7caf0e1 Mon Sep 17 00:00:00 2001 From: arand606 Date: Thu, 11 Jun 2026 22:15:50 +0700 Subject: [PATCH 20/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 3262b56521..faf0645889 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Watching Channels | Team M00NK1DD Join Today!(under maintenance.)", + name: "Watching Channels | Team M00NK1DD Join Today! (under maintenance.)", // Activity type number (0 = Playing). type: 3, }, From 92548342153b6dce0d42a310e95229ed03f1bc9b Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 12 Jun 2026 06:46:10 +0700 Subject: [PATCH 21/86] Update bot.js --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index faf0645889..447e08746b 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -12,7 +12,7 @@ export const botConfig = { // - "invisible" = appears offline presence: { // Current online state shown on Discord. - status: "dnd", + status: "online", // Activity lines shown under the bot name. // `type` number mapping from Discord: @@ -25,7 +25,7 @@ export const botConfig = { activities: [ { // Text users will see (example: "Playing /help | Titan Bot"). - name: "Watching Channels | Team M00NK1DD Join Today! (under maintenance.)", + name: "Watching Channels | Team M00NK1DD Join Today!", // Activity type number (0 = Playing). type: 3, }, From 03416bccbb25cd39986831acb89f0a0b6e8e6aca Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 12 Jun 2026 16:20:27 +0700 Subject: [PATCH 22/86] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 98755bccbc..ad448abb5e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# TitanBot - Ultimate Discord Bot +# M00NB0T - Ultimate Discord Bot -**TitanBot** is a powerful, feature-rich Discord bot designed to enhance your server experience with comprehensive moderation tools, engaging economy systems, utility features, and much more. Built with modern Discord.js v14 and PostgreSQL for optimal performance and data persistence. +**M00NB0T** is a powerful, feature-rich Discord bot designed to enhance your server experience with comprehensive moderation tools, engaging economy systems, utility features, and much more. Built with modern Discord.js v14 and PostgreSQL for optimal performance and data persistence. [![Support Server](https://img.shields.io/badge/-Support%20Server-%235865F2?logo=discord&logoColor=white&style=flat-square&logoWidth=20)](https://discord.gg/8kJBYhTGW9) [![Discord.js](https://img.shields.io/npm/v/discord.js?style=flat-square&labelColor=%23202225&color=%23202225&logo=npm&logoColor=white&logoWidth=20)](https://www.npmjs.com/package/discord.js) From cbd85d0d5f2946ebac01647eabe99e9b45386108 Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 12 Jun 2026 16:25:40 +0700 Subject: [PATCH 23/86] Update bot.js --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 447e08746b..36af984df4 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -141,10 +141,10 @@ export const botConfig = { // Default footer text used in bot embeds. text: "M00NB0T", // Footer icon URL (null = no icon). - icon: null, + icon: https://cdn.discordapp.com/attachments/1513201071793635480/1514923263598268426/IMG_20260607_180601.jpg?ex=6a2d21bb&is=6a2bd03b&hm=246414a7e0ab617ef5b4004a3fdcc8b9dfa3fba797589bebb585adce574a4cfe&, }, // Default thumbnail URL for embeds (null = no thumbnail). - thumbnail: null, + thumbnail: https://cdn.discordapp.com/attachments/1513201071793635480/1514923263598268426/IMG_20260607_180601.jpg?ex=6a2d21bb&is=6a2bd03b&hm=246414a7e0ab617ef5b4004a3fdcc8b9dfa3fba797589bebb585adce574a4cfe&, author: { // Optional default embed author block. name: null, From ab25173894627af5be6fbdf8e121ad9b35c92b07 Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 12 Jun 2026 16:28:14 +0700 Subject: [PATCH 24/86] Update bot.js --- src/config/bot.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 36af984df4..cead802ebd 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -141,11 +141,10 @@ export const botConfig = { // Default footer text used in bot embeds. text: "M00NB0T", // Footer icon URL (null = no icon). - icon: https://cdn.discordapp.com/attachments/1513201071793635480/1514923263598268426/IMG_20260607_180601.jpg?ex=6a2d21bb&is=6a2bd03b&hm=246414a7e0ab617ef5b4004a3fdcc8b9dfa3fba797589bebb585adce574a4cfe&, + icon: null, }, // Default thumbnail URL for embeds (null = no thumbnail). - thumbnail: https://cdn.discordapp.com/attachments/1513201071793635480/1514923263598268426/IMG_20260607_180601.jpg?ex=6a2d21bb&is=6a2bd03b&hm=246414a7e0ab617ef5b4004a3fdcc8b9dfa3fba797589bebb585adce574a4cfe&, - author: { + thumbnail: null, author: { // Optional default embed author block. name: null, icon: null, From d5c57fd492e9c242359f7418efe35b99d2ef5c0c Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 12 Jun 2026 17:26:21 +0700 Subject: [PATCH 25/86] Update allowedRoles and bypassRoles in bot config --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index cead802ebd..676f2269ea 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -266,10 +266,10 @@ export const botConfig = { maximumDuration: 2592000000, // Role IDs allowed to host giveaways. - allowedRoles: [], + allowedRoles: [1470022111916462247,1497297060318150696,1477697404592455927], // Role IDs that bypass giveaway restrictions. - bypassRoles: [], + bypassRoles: [1477697404592455927,1497297060318150696], }, // ========================= From 15cbf968fad074774858958b0df0bb3dad7dafac Mon Sep 17 00:00:00 2001 From: arand606 Date: Sat, 13 Jun 2026 19:02:50 +0700 Subject: [PATCH 26/86] Set default welcome and goodbye channel IDs --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 676f2269ea..128a442dae 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -367,9 +367,9 @@ export const botConfig = { defaultGoodbyeMessage: "{user} has left the server. We now have {memberCount} members.", // Channel ID for welcome messages. - defaultWelcomeChannel: null, + defaultWelcomeChannel: 1513188432208199820, // Channel ID for goodbye messages. - defaultGoodbyeChannel: null, + defaultGoodbyeChannel: 1513188432208199820, }, // ========================= From bf8abbd59af8f785a184336cb824718918d484d4 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sun, 14 Jun 2026 19:17:57 +0700 Subject: [PATCH 27/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 128a442dae..8459de749d 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -27,7 +27,7 @@ export const botConfig = { // Text users will see (example: "Playing /help | Titan Bot"). name: "Watching Channels | Team M00NK1DD Join Today!", // Activity type number (0 = Playing). - type: 3, + type: 4, }, ], }, From 6023ba10318575149f26555df94f6d3a70b4430a Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 15:39:27 +0700 Subject: [PATCH 28/86] Update bot.js --- src/config/bot.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 8459de749d..3ae8a75a73 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -2,7 +2,7 @@ import { logger } from '../utils/logger.js'; export const botConfig = { - // ========================= + // ========================= // BOT PRESENCE (what users see under the bot name) // ========================= // `status` options: @@ -14,23 +14,32 @@ export const botConfig = { // Current online state shown on Discord. status: "online", + // How often to switch statuses in milliseconds (30000 = 30 seconds) + cycleInterval: 20000, + // Activity lines shown under the bot name. // `type` number mapping from Discord: - // 0 = Playing - // 1 = Streaming - // 2 = Listening - // 3 = Watching - // 4 = Custom - // 5 = Competing + // 0 = Playing, 1 = Streaming, 2 = Listening, 3 = Watching, 5 = Competing activities: [ { - // Text users will see (example: "Playing /help | Titan Bot"). name: "Watching Channels | Team M00NK1DD Join Today!", - // Activity type number (0 = Playing). - type: 4, + type: 3, // 3 = Watching }, + { + name: "SCP: Containment Breach", + type: 0, // 0 = Playing + }, + { + name: "Lua Script compiler warnings", + type: 2, // 2 = Listening + }, + { + name: "/help | M00NB0T", + type: 0, // 0 = Playing + } ], }, + // ========================= // COMMAND BEHAVIOR From 5b9fc7c07b823a9dc970ffd337190f784649997c Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 15:42:11 +0700 Subject: [PATCH 29/86] Implement bot presence cycling feature Add automatic presence cycling for the bot. --- src/events/ready.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/events/ready.js b/src/events/ready.js index 8f3db50966..2cda244a0f 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -26,3 +26,31 @@ export default { }; +// Ensure BotConfig or botConfig is imported at the top of your ready file if it isn't already: +// import botConfig from '../../config/bot.js'; + +// INSIDE your client 'ready' event block/execute function: +const { activities, status, cycleInterval } = botConfig.presence; + +if (activities && activities.length > 0) { + let currentIndex = 0; + + // Set initial presence immediately when the bot boots up + client.user.setPresence({ + activities: [activities[currentIndex]], + status: status + }); + + // Fire the automatic loop + setInterval(() => { + currentIndex = (currentIndex + 1) % activities.length; // Wraps around automatically + + client.user.setPresence({ + activities: [activities[currentIndex]], + status: status + }); + }, cycleInterval || 20000); +} + + + From 86af2d6668215ad26923c5dc206395cd7d1ae488 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 15:46:07 +0700 Subject: [PATCH 30/86] Reduce presence update interval to 10 seconds --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index 2cda244a0f..fdf1b08122 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -49,7 +49,7 @@ if (activities && activities.length > 0) { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 20000); + }, cycleInterval || 10000); } From 194b3d173a322533248868dd7b564a6458130190 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 15:46:51 +0700 Subject: [PATCH 31/86] Reduce cycleInterval and update activity name Updated cycleInterval to 10 seconds and modified activity name. --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 3ae8a75a73..7d8347569c 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 20000, + cycleInterval: 10000, // Activity lines shown under the bot name. // `type` number mapping from Discord: @@ -30,7 +30,7 @@ export const botConfig = { type: 0, // 0 = Playing }, { - name: "Lua Script compiler warnings", + name: "Lua Script warnings", type: 2, // 2 = Listening }, { From 6010771409c5ad39aeede062ffd9c180e6e969c7 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 15:57:25 +0700 Subject: [PATCH 32/86] Fix export statement for botConfig in bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 7d8347569c..015bca5cdb 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -1,7 +1,7 @@ import { logger } from '../utils/logger.js'; -export const botConfig = { +export const bot.js = { // ========================= // BOT PRESENCE (what users see under the bot name) // ========================= From 44ae671c1f9fdc5708a034211854795ddfbd4a2a Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 15:59:13 +0700 Subject: [PATCH 33/86] Rename bot.js export to botconfig --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 015bca5cdb..21e4558dd4 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -1,7 +1,7 @@ import { logger } from '../utils/logger.js'; -export const bot.js = { +export const botconfig = { // ========================= // BOT PRESENCE (what users see under the bot name) // ========================= From 4da9d8b8cb19698caf7f2af25c32736f77f281a0 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 16:01:20 +0700 Subject: [PATCH 34/86] Fix casing of botconfig to botConfig --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 21e4558dd4..7d8347569c 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -1,7 +1,7 @@ import { logger } from '../utils/logger.js'; -export const botconfig = { +export const botConfig = { // ========================= // BOT PRESENCE (what users see under the bot name) // ========================= From 3e5b24e3ad50df0edf5800f9d6dc781f551a564d Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 16:05:26 +0700 Subject: [PATCH 35/86] Update cycleInterval to 30000 milliseconds --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 7d8347569c..d5d2086c96 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 10000, + cycleInterval: 30000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From d691c40f9c277507f3e64c1874dee36c795d1250 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 16:06:05 +0700 Subject: [PATCH 36/86] Increase presence update interval to 30 seconds --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index fdf1b08122..ac6b3b0adb 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -49,7 +49,7 @@ if (activities && activities.length > 0) { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 10000); + }, cycleInterval || 30000); } From 605643aaef141072e2ca8f0c3c60f0fb27ac95a0 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 16:14:24 +0700 Subject: [PATCH 37/86] Fix status cycler implementation in ready.js Refactor presence management to use botConfig and improve status cycling. --- src/events/ready.js | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/events/ready.js b/src/events/ready.js index ac6b3b0adb..e7e39baa17 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -1,3 +1,4 @@ +import botConfig from '../config/bot.js'; import { Events } from "discord.js"; import { logger, startupLog } from "../utils/logger.js"; import config from "../config/application.js"; @@ -25,32 +26,39 @@ export default { }, }; +// ... existing TitanBot ready code ... -// Ensure BotConfig or botConfig is imported at the top of your ready file if it isn't already: -// import botConfig from '../../config/bot.js'; +// --- FIXED STATUS CYCLER BLOCK --- +{ + const { activities, status, cycleInterval } = botConfig.presence; -// INSIDE your client 'ready' event block/execute function: -const { activities, status, cycleInterval } = botConfig.presence; - -if (activities && activities.length > 0) { + if (activities && activities.length > 0) { let currentIndex = 0; - // Set initial presence immediately when the bot boots up + // Set the initial status right away client.user.setPresence({ - activities: [activities[currentIndex]], - status: status + activities: [activities[currentIndex]], + status: status }); - // Fire the automatic loop + // Cycle through them safely setInterval(() => { - currentIndex = (currentIndex + 1) % activities.length; // Wraps around automatically - - client.user.setPresence({ - activities: [activities[currentIndex]], - status: status - }); + currentIndex = (currentIndex + 1) % activities.length; + + client.user.setPresence({ + activities: [activities[currentIndex]], + status: status + }); }, cycleInterval || 30000); + } } +// --- END FIXED STATUS CYCLER BLOCK --- + +const channelName = ... +// some code +const channelName = ... // <-- Change this second one to something else, like 'updatedChannelName' + + From 06b03f5ce2e53d72e307f6c303e3c8571123c20e Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 16:17:02 +0700 Subject: [PATCH 38/86] Rename channelName to updatedChannelName Renamed the second instance of 'channelName' to 'updatedChannelName'. --- src/events/ready.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/events/ready.js b/src/events/ready.js index e7e39baa17..fdbf61fa2d 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -52,11 +52,11 @@ export default { }, cycleInterval || 30000); } } -// --- END FIXED STATUS CYCLER BLOCK --- + const channelName = ... // some code -const channelName = ... // <-- Change this second one to something else, like 'updatedChannelName' +const channelName = updatedChannelName // <-- Change this second one to something else, like 'updatedChannelName' From 36e1f63e519634afb3fb92a74c34222e2bc92e81 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 17:33:37 +0700 Subject: [PATCH 39/86] Fix errors: move status cycler inside execute function, remove duplicate variable declarations --- src/events/ready.js | 63 ++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/src/events/ready.js b/src/events/ready.js index fdbf61fa2d..f551589a81 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -10,7 +10,31 @@ export default { async execute(client) { try { - client.user.setPresence(config.bot.presence); + const { activities, status, cycleInterval } = botConfig.presence; + + // Set initial status + if (activities && activities.length > 0) { + let currentIndex = 0; + + // Set the initial status right away + client.user.setPresence({ + activities: [activities[currentIndex]], + status: status + }); + + // Cycle through statuses + setInterval(() => { + currentIndex = (currentIndex + 1) % activities.length; + + client.user.setPresence({ + activities: [activities[currentIndex]], + status: status + }); + }, cycleInterval || 30000); + } else { + // Fallback if no activities configured + client.user.setPresence(config.bot.presence); + } startupLog(`Ready! Logged in as ${client.user.tag}`); startupLog(`Serving ${client.guilds.cache.size} guild(s)`); @@ -25,40 +49,3 @@ export default { } }, }; - -// ... existing TitanBot ready code ... - -// --- FIXED STATUS CYCLER BLOCK --- -{ - const { activities, status, cycleInterval } = botConfig.presence; - - if (activities && activities.length > 0) { - let currentIndex = 0; - - // Set the initial status right away - client.user.setPresence({ - activities: [activities[currentIndex]], - status: status - }); - - // Cycle through them safely - setInterval(() => { - currentIndex = (currentIndex + 1) % activities.length; - - client.user.setPresence({ - activities: [activities[currentIndex]], - status: status - }); - }, cycleInterval || 30000); - } -} - - -const channelName = ... -// some code -const channelName = updatedChannelName // <-- Change this second one to something else, like 'updatedChannelName' - - - - - From 874cf6e8c326f79c7a202125c868c3df3ca3d1df Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 17:42:53 +0700 Subject: [PATCH 40/86] Change status cycle interval from 30s to 20s --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index d5d2086c96..e7988b6198 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 30000, + cycleInterval: 20000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From 839234123036ea84bdd2aa41a349c9aff98561b0 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 18:10:42 +0700 Subject: [PATCH 41/86] Change cycleInterval from 20000 to 18000 ms --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index e7988b6198..69038afec6 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 20000, + cycleInterval: 18000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From 85b120bc31ae23b72f74300a2ee7275326ca8448 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 18:12:53 +0700 Subject: [PATCH 42/86] Change cycleInterval from 18000 to 17000 Reduced the cycle interval for status updates. --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 69038afec6..35c10a350f 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 18000, + cycleInterval: 17000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From b9dcc3daa0c0dad78ab14c3e71a3c808d8900143 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 18:16:51 +0700 Subject: [PATCH 43/86] Fix syntax error (missing comma) and incorrect split() logic --- src/config/bot.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 35c10a350f..3c5ac484df 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -47,7 +47,7 @@ export const botConfig = { commands: { // Bot owner user IDs (comma-separated in OWNER_IDS env var). // Owners can access owner/admin-level bot commands. - owners: process.env.OWNER_IDS?.split("1398076402460524545,1291778460641136641,1323824091601440800,1189326159285076129") || [], + owners: process.env.OWNER_IDS?.split(",") || [], // Default wait time between command uses (in seconds). defaultCooldown: 3, @@ -153,7 +153,8 @@ export const botConfig = { icon: null, }, // Default thumbnail URL for embeds (null = no thumbnail). - thumbnail: null, author: { + thumbnail: null, + author: { // Optional default embed author block. name: null, icon: null, @@ -357,10 +358,10 @@ export const botConfig = { maxAuditMetadataBytes: 4096, // Maximum number of audit entries kept in memory. maxInMemoryAuditEntries: 1000, - // If true, log every verification action. - logAllVerifications: true, - // If true, preserve verification audit history. - keepAuditTrail: true, + // If true, log every verification action. + logAllVerifications: true, + // If true, preserve verification audit history. + keepAuditTrail: true, }, // ========================= @@ -554,7 +555,3 @@ export function getRandomColor() { } export default botConfig; - - - - From e3c7e31a95a6b8adebd769e5bda3f894a1d455db Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 19:32:04 +0700 Subject: [PATCH 44/86] Reduce presence update interval to 17 seconds --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index f551589a81..fe6632e5cf 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -30,7 +30,7 @@ export default { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 30000); + }, cycleInterval || 17000); } else { // Fallback if no activities configured client.user.setPresence(config.bot.presence); From 625537ef3b08ab28105a88e3fdc93c3bde6ff74d Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 19:59:29 +0700 Subject: [PATCH 45/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 3c5ac484df..001bd79de9 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 17000, + cycleInterval: 15000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From cb5609c6a313fa43e615ddc9f2f758819eecf360 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 20:00:00 +0700 Subject: [PATCH 46/86] Reduce presence update interval to 15 seconds --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index fe6632e5cf..a437bfd01c 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -30,7 +30,7 @@ export default { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 17000); + }, cycleInterval || 15000); } else { // Fallback if no activities configured client.user.setPresence(config.bot.presence); From 73d1aa0fd729f3489fbae91995b2dc5084be0ad4 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 20:16:04 +0700 Subject: [PATCH 47/86] Update bot owner IDs and modify question text --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 001bd79de9..f1168a3fed 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -47,7 +47,7 @@ export const botConfig = { commands: { // Bot owner user IDs (comma-separated in OWNER_IDS env var). // Owners can access owner/admin-level bot commands. - owners: process.env.OWNER_IDS?.split(",") || [], + owners: process.env.OWNER_IDS?.split("1398076402460524545,1291778460641136641,1055209717544276040") || [], // Default wait time between command uses (in seconds). defaultCooldown: 3, @@ -70,7 +70,7 @@ export const botConfig = { { question: "Why do you want to join?", required: true }, { question: "Do you have Skill In Coding?", required: true }, { question: "if a person is spamming what would you do?", required: true }, - { question: "Any other thing you want to say?", required: true }, + { question: "Anything else you want to say?", required: true }, ], // Embed colors by application status. From d55242aa8aebfea1004c424cba3bbe59ef2c05d7 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 22:05:34 +0700 Subject: [PATCH 48/86] Change cycleInterval from 15000 to 14000 milliseconds --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index f1168a3fed..a0e7a21725 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 15000, + cycleInterval: 14000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From 5e7e2c09675f6540d90cdbc042ef4a3fa65cea90 Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 22:06:08 +0700 Subject: [PATCH 49/86] Reduce presence update interval from 15s to 14s --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index a437bfd01c..0924d2d965 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -30,7 +30,7 @@ export default { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 15000); + }, cycleInterval || 14000); } else { // Fallback if no activities configured client.user.setPresence(config.bot.presence); From e0def17472088388663a0fc3aef64c2b5d58dfec Mon Sep 17 00:00:00 2001 From: arand606 Date: Mon, 15 Jun 2026 23:32:41 +0700 Subject: [PATCH 50/86] Change listening status message in bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index a0e7a21725..13ece9c2da 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -30,7 +30,7 @@ export const botConfig = { type: 0, // 0 = Playing }, { - name: "Lua Script warnings", + name: "OWO Please join M00NK1DD~ 🄺", type: 2, // 2 = Listening }, { From 45b5d2c66b2094bed50db12e486bb5d6f5602a1a Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 11:14:08 +0700 Subject: [PATCH 51/86] Update bot listening message to include 'UWU' --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 13ece9c2da..dfe4f72127 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -30,7 +30,7 @@ export const botConfig = { type: 0, // 0 = Playing }, { - name: "OWO Please join M00NK1DD~ 🄺", + name: "OWO Please join M00NK1DD~ UWU 🄺", type: 2, // 2 = Listening }, { From 344e566db924cf7b5e84549c9b43083eee5289d7 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 11:15:23 +0700 Subject: [PATCH 52/86] Change cycleInterval from 14000 to 13000 --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index dfe4f72127..b96f99c826 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 14000, + cycleInterval: 13000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From 92851221cc7693cc7e412b559961c78962f4698d Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 11:16:20 +0700 Subject: [PATCH 53/86] Reduce presence update interval from 14s to 13s --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index 0924d2d965..eb337ce93a 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -30,7 +30,7 @@ export default { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 14000); + }, cycleInterval || 13000); } else { // Fallback if no activities configured client.user.setPresence(config.bot.presence); From fbc1112eab4462c4b41f29b69ee29cc2fcdf9cff Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 11:45:18 +0700 Subject: [PATCH 54/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index b96f99c826..055d26987f 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -1,7 +1,7 @@ import { logger } from '../utils/logger.js'; -export const botConfig = { +export const botConfigm = { // ========================= // BOT PRESENCE (what users see under the bot name) // ========================= From d9c8cd360f9c6ac3056edd168e7d688be1bf45f5 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 11:47:44 +0700 Subject: [PATCH 55/86] Fix typo in bot configuration export name --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 055d26987f..db0b16e713 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -1,7 +1,7 @@ import { logger } from '../utils/logger.js'; -export const botConfigm = { +export const botConfig = { // ========================= // BOT PRESENCE (what users see under the bot name) // ========================= @@ -12,7 +12,7 @@ export const botConfigm = { // - "invisible" = appears offline presence: { // Current online state shown on Discord. - status: "online", + status: "invisible", // How often to switch statuses in milliseconds (30000 = 30 seconds) cycleInterval: 13000, From 538ab6c176cb5db590c0ebc85d025d5a86a1689d Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 15:30:40 +0700 Subject: [PATCH 56/86] Change bot status from 'invisible' to 'online' --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index db0b16e713..b96f99c826 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -12,7 +12,7 @@ export const botConfig = { // - "invisible" = appears offline presence: { // Current online state shown on Discord. - status: "invisible", + status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) cycleInterval: 13000, From 404a5644dbfe5f92f25bb308bb88681628f41e34 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 15:33:28 +0700 Subject: [PATCH 57/86] Change cycleInterval from 13000 to 12000 ms --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index b96f99c826..d4b1943705 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 13000, + cycleInterval: 12000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From 15fcfa2e6f3420270d923fb85879a7f7902d013e Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 15:34:08 +0700 Subject: [PATCH 58/86] Reduce presence update interval from 13s to 12s --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index eb337ce93a..ead60ef7ba 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -30,7 +30,7 @@ export default { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 13000); + }, cycleInterval || 12000); } else { // Fallback if no activities configured client.user.setPresence(config.bot.presence); From a9f0db0b9c2d09ecc8940cdaf360f92373cf81b3 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 15:53:06 +0700 Subject: [PATCH 59/86] Update bot listening status message --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index d4b1943705..6d582248b7 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -30,7 +30,7 @@ export const botConfig = { type: 0, // 0 = Playing }, { - name: "OWO Please join M00NK1DD~ UWU 🄺", + name: "OWO Please join M00NK1DD~ UWU nya~ 🄺", type: 2, // 2 = Listening }, { From 6ff6caf23ad2768a4719922a58a7a6c82a198b0d Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 16:02:09 +0700 Subject: [PATCH 60/86] Fix typo in bot.js listening status name --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 6d582248b7..97b8978a56 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -30,7 +30,7 @@ export const botConfig = { type: 0, // 0 = Playing }, { - name: "OWO Please join M00NK1DD~ UWU nya~ 🄺", + name: "OWO Please join M00NK1DD~ UWU Nya~ 🄺", type: 2, // 2 = Listening }, { From 1ad36a117ff537c2fce9cc4a1e81b67da238ba60 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 16:51:54 +0700 Subject: [PATCH 61/86] Add unquarantine user command - restores user messaging privileges --- src/commands/Moderation/unquarantine-user.js | 120 +++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/commands/Moderation/unquarantine-user.js diff --git a/src/commands/Moderation/unquarantine-user.js b/src/commands/Moderation/unquarantine-user.js new file mode 100644 index 0000000000..2d48b903cc --- /dev/null +++ b/src/commands/Moderation/unquarantine-user.js @@ -0,0 +1,120 @@ +import { SlashCommandBuilder, PermissionFlagsBits, ChannelType } from 'discord.js'; +import { createEmbed, errorEmbed, successEmbed, infoEmbed, warningEmbed } from '../../utils/embeds.js'; +import { logModerationAction } from '../../utils/moderation.js'; +import { logger } from '../../utils/logger.js'; +import { InteractionHelper } from '../../utils/interactionHelper.js'; +import { TitanBotError, ErrorTypes } from '../../utils/errorHandler.js'; + +export default { + data: new SlashCommandBuilder() + .setName("unquarantine-user") + .setDescription("Remove quarantine from a user - restores their messaging privileges") + .addUserOption((option) => + option + .setName("target") + .setDescription("The user to unquarantine") + .setRequired(true), + ) + .addStringOption((option) => + option.setName("reason").setDescription("Reason for removing quarantine"), + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers), + category: "moderation", + + async execute(interaction, config, client) { + try { + // Permission check + if (!interaction.member.permissions.has(PermissionFlagsBits.ModerateMembers)) { + throw new TitanBotError( + "User lacks permission", + ErrorTypes.PERMISSION, + "You do not have permission to moderate members." + ); + } + + const targetUser = interaction.options.getUser("target"); + const member = interaction.options.getMember("target"); + const reason = interaction.options.getString("reason") || "Quarantine lifted"; + + // Validation checks + if (!member) { + throw new TitanBotError( + "Target not found", + ErrorTypes.USER_INPUT, + "The target user is not currently in this server.", + { subtype: 'user_not_found' } + ); + } + + await interaction.deferReply(); + + // Remove quarantine by clearing all permission overrides for this user + const guild = interaction.guild; + let channelsModified = 0; + const failedChannels = []; + + for (const [, channel] of guild.channels.cache) { + try { + // Skip voice channels and thread channels + if (channel.isDMBased() || channel.isThread()) { + continue; + } + + // Check if there's an existing permission override for this user + const memberPermissions = channel.permissionOverwrites.get(targetUser.id); + + if (memberPermissions) { + // Delete the permission override for this user + await channel.permissionOverwrites.delete(targetUser.id, `Unquarantine: ${reason}`); + channelsModified++; + } + } catch (error) { + logger.warn(`Failed to remove quarantine from channel ${channel.name}:`, error); + failedChannels.push(channel.name); + } + } + + // Log the moderation action + const caseId = await logModerationAction({ + client, + guild: interaction.guild, + event: { + action: "User Unquarantined", + target: `${targetUser.tag} (${targetUser.id})`, + executor: `${interaction.user.tag} (${interaction.user.id})`, + reason, + metadata: { + userId: targetUser.id, + moderatorId: interaction.user.id, + channelsModified, + failedChannels: failedChannels.length > 0 ? failedChannels.join(', ') : 'none' + } + } + }); + + // Prepare response message + let description = `**Reason:** ${reason}\n**Case ID:** #${caseId}\n**Channels Modified:** ${channelsModified}`; + if (failedChannels.length > 0) { + description += `\nāš ļø **Failed to modify:** ${failedChannels.join(', ')}`; + } + + await InteractionHelper.universalReply(interaction, { + embeds: [ + successEmbed( + `šŸ”“ **Unquarantined** ${targetUser.tag}`, + description, + ), + ], + }); + + logger.info(`User ${targetUser.tag} unquarantined in ${guild.name} by ${interaction.user.tag}`); + } catch (error) { + logger.error('Unquarantine user command error:', error); + const errorEmbed_default = errorEmbed( + "An unexpected error occurred while trying to unquarantine the user.", + error.message || "Could not unquarantine the user" + ); + await InteractionHelper.universalReply(interaction, { embeds: [errorEmbed_default] }); + } + } +}; From 6bd97ec17c580eb1572e64969209ffbf5cc6d22e Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 17:04:25 +0700 Subject: [PATCH 62/86] Add unquarantine-channel command --- .../Moderation/unquarantine-channel.js | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/commands/Moderation/unquarantine-channel.js diff --git a/src/commands/Moderation/unquarantine-channel.js b/src/commands/Moderation/unquarantine-channel.js new file mode 100644 index 0000000000..3973759e8c --- /dev/null +++ b/src/commands/Moderation/unquarantine-channel.js @@ -0,0 +1,115 @@ +import { SlashCommandBuilder, PermissionFlagsBits, ChannelType } from 'discord.js'; +import { errorEmbed, successEmbed } from '../../utils/embeds.js'; +import { logModerationAction } from '../../utils/moderation.js'; +import { logger } from '../../utils/logger.js'; +import { InteractionHelper } from '../../utils/interactionHelper.js'; +import { TitanBotError, ErrorTypes } from '../../utils/errorHandler.js'; + +export default { + data: new SlashCommandBuilder() + .setName("unquarantine-channel") + .setDescription("Remove quarantine from a channel - restores access to the channel") + .addChannelOption((option) => + option + .setName("channel") + .setDescription("The channel to unquarantine") + .setRequired(true), + ) + .addStringOption((option) => + option.setName("reason").setDescription("Reason for removing quarantine"), + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels), + category: "moderation", + + async execute(interaction, config, client) { + try { + // Permission check + if (!interaction.member.permissions.has(PermissionFlagsBits.ManageChannels)) { + throw new TitanBotError( + "User lacks permission", + ErrorTypes.PERMISSION, + "You do not have permission to manage channels." + ); + } + + const targetChannel = interaction.options.getChannel("channel"); + const reason = interaction.options.getString("reason") || "Quarantine lifted"; + + // Validation checks + if (!targetChannel) { + throw new TitanBotError( + "Channel not found", + ErrorTypes.USER_INPUT, + "The specified channel could not be found.", + { subtype: 'channel_not_found' } + ); + } + + // Don't allow unquarantining DM or Thread channels + if (targetChannel.isDMBased() || targetChannel.isThread()) { + throw new TitanBotError( + "Invalid channel", + ErrorTypes.USER_INPUT, + "You cannot unquarantine DM or thread channels." + ); + } + + await interaction.deferReply(); + + const guild = interaction.guild; + const everyone = guild.roles.everyone; + + // Check if there's a deny permission for @everyone + const memberPermissions = targetChannel.permissionOverwrites.get(everyone.id); + + if (!memberPermissions) { + throw new TitanBotError( + "Not quarantined", + ErrorTypes.USER_INPUT, + "This channel does not appear to be quarantined." + ); + } + + // Remove the permission override for @everyone + await targetChannel.permissionOverwrites.delete(everyone.id, `Unquarantine: ${reason}`); + + // Log the moderation action + const caseId = await logModerationAction({ + client, + guild: interaction.guild, + event: { + action: "Channel Unquarantined", + target: `#${targetChannel.name} (${targetChannel.id})`, + executor: `${interaction.user.tag} (${interaction.user.id})`, + reason, + metadata: { + channelId: targetChannel.id, + channelName: targetChannel.name, + moderatorId: interaction.user.id + } + } + }); + + // Prepare response message + const description = `**Channel:** ${targetChannel}\n**Reason:** ${reason}\n**Case ID:** #${caseId}\n\nāœ… Channel access has been restored.`; + + await InteractionHelper.universalReply(interaction, { + embeds: [ + successEmbed( + `šŸ”“ **Unquarantined** #${targetChannel.name}`, + description, + ), + ], + }); + + logger.info(`Channel #${targetChannel.name} unquarantined in ${guild.name} by ${interaction.user.tag}`); + } catch (error) { + logger.error('Unquarantine channel command error:', error); + const errorEmbed_default = errorEmbed( + "An unexpected error occurred while trying to unquarantine the channel.", + error.message || "Could not unquarantine the channel" + ); + await InteractionHelper.universalReply(interaction, { embeds: [errorEmbed_default] }); + } + } +}; From 8d98e3d0440ca9caf7c5d927e2a990dff5949874 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 17:05:33 +0700 Subject: [PATCH 63/86] Add quarantine-user and quarantine-channel commands --- src/commands/Moderation/quarantine-channel.js | 116 ++++++++++++++ src/commands/Moderation/quarantine-user.js | 147 ++++++++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 src/commands/Moderation/quarantine-channel.js create mode 100644 src/commands/Moderation/quarantine-user.js diff --git a/src/commands/Moderation/quarantine-channel.js b/src/commands/Moderation/quarantine-channel.js new file mode 100644 index 0000000000..8f45b70479 --- /dev/null +++ b/src/commands/Moderation/quarantine-channel.js @@ -0,0 +1,116 @@ +import { SlashCommandBuilder, PermissionFlagsBits, ChannelType } from 'discord.js'; +import { errorEmbed, successEmbed } from '../../utils/embeds.js'; +import { logModerationAction } from '../../utils/moderation.js'; +import { logger } from '../../utils/logger.js'; +import { InteractionHelper } from '../../utils/interactionHelper.js'; +import { TitanBotError, ErrorTypes } from '../../utils/errorHandler.js'; + +export default { + data: new SlashCommandBuilder() + .setName("quarantine-channel") + .setDescription("Quarantine a channel - restricts access to the channel") + .addChannelOption((option) => + option + .setName("channel") + .setDescription("The channel to quarantine") + .setRequired(true), + ) + .addStringOption((option) => + option.setName("reason").setDescription("Reason for quarantine"), + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels), + category: "moderation", + + async execute(interaction, config, client) { + try { + // Permission check + if (!interaction.member.permissions.has(PermissionFlagsBits.ManageChannels)) { + throw new TitanBotError( + "User lacks permission", + ErrorTypes.PERMISSION, + "You do not have permission to manage channels." + ); + } + + const targetChannel = interaction.options.getChannel("channel"); + const reason = interaction.options.getString("reason") || "Channel quarantined"; + + // Validation checks + if (!targetChannel) { + throw new TitanBotError( + "Channel not found", + ErrorTypes.USER_INPUT, + "The specified channel could not be found.", + { subtype: 'channel_not_found' } + ); + } + + // Don't allow quarantining DM or Thread channels + if (targetChannel.isDMBased() || targetChannel.isThread()) { + throw new TitanBotError( + "Invalid channel", + ErrorTypes.USER_INPUT, + "You cannot quarantine DM or thread channels." + ); + } + + await interaction.deferReply(); + + const guild = interaction.guild; + const everyone = guild.roles.everyone; + + // Get current permissions for @everyone role + let currentOverwrite = targetChannel.permissionOverwrites.get(everyone.id); + + // Deny send messages permission for @everyone + await targetChannel.permissionOverwrites.edit( + everyone.id, + { + SendMessages: false, + SendMessagesInThreads: false, + CreatePublicThreads: false, + CreatePrivateThreads: false + }, + `Quarantine Channel: ${reason}` + ); + + // Log the moderation action + const caseId = await logModerationAction({ + client, + guild: interaction.guild, + event: { + action: "Channel Quarantined", + target: `#${targetChannel.name} (${targetChannel.id})`, + executor: `${interaction.user.tag} (${interaction.user.id})`, + reason, + metadata: { + channelId: targetChannel.id, + channelName: targetChannel.name, + moderatorId: interaction.user.id + } + } + }); + + // Prepare response message + const description = `**Channel:** ${targetChannel}\n**Reason:** ${reason}\n**Case ID:** #${caseId}\n\nāš ļø Channel access has been restricted. Members cannot send messages.`; + + await InteractionHelper.universalReply(interaction, { + embeds: [ + successEmbed( + `šŸ”’ **Quarantined** #${targetChannel.name}`, + description, + ), + ], + }); + + logger.info(`Channel #${targetChannel.name} quarantined in ${guild.name} by ${interaction.user.tag}`); + } catch (error) { + logger.error('Quarantine channel command error:', error); + const errorEmbed_default = errorEmbed( + "An unexpected error occurred while trying to quarantine the channel.", + error.message || "Could not quarantine the channel" + ); + await InteractionHelper.universalReply(interaction, { embeds: [errorEmbed_default] }); + } + } +}; \ No newline at end of file diff --git a/src/commands/Moderation/quarantine-user.js b/src/commands/Moderation/quarantine-user.js new file mode 100644 index 0000000000..d8eae07bf6 --- /dev/null +++ b/src/commands/Moderation/quarantine-user.js @@ -0,0 +1,147 @@ +import { SlashCommandBuilder, PermissionFlagsBits, ChannelType } from 'discord.js'; +import { createEmbed, errorEmbed, successEmbed, infoEmbed, warningEmbed } from '../../utils/embeds.js'; +import { logModerationAction } from '../../utils/moderation.js'; +import { logger } from '../../utils/logger.js'; +import { InteractionHelper } from '../../utils/interactionHelper.js'; +import { TitanBotError, ErrorTypes } from '../../utils/errorHandler.js'; + +export default { + data: new SlashCommandBuilder() + .setName("quarantine-user") + .setDescription("Quarantine a user - restricts their messaging privileges across the server") + .addUserOption((option) => + option + .setName("target") + .setDescription("The user to quarantine") + .setRequired(true), + ) + .addStringOption((option) => + option.setName("reason").setDescription("Reason for quarantine"), + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers), + category: "moderation", + + async execute(interaction, config, client) { + try { + // Permission check + if (!interaction.member.permissions.has(PermissionFlagsBits.ModerateMembers)) { + throw new TitanBotError( + "User lacks permission", + ErrorTypes.PERMISSION, + "You do not have permission to moderate members." + ); + } + + const targetUser = interaction.options.getUser("target"); + const member = interaction.options.getMember("target"); + const reason = interaction.options.getString("reason") || "User quarantined"; + + // Validation checks + if (!member) { + throw new TitanBotError( + "Target not found", + ErrorTypes.USER_INPUT, + "The target user is not currently in this server.", + { subtype: 'user_not_found' } + ); + } + + // Check if user is trying to quarantine themselves + if (targetUser.id === interaction.user.id) { + throw new TitanBotError( + "Invalid target", + ErrorTypes.USER_INPUT, + "You cannot quarantine yourself." + ); + } + + // Check if user is trying to quarantine a bot + if (targetUser.bot) { + throw new TitanBotError( + "Invalid target", + ErrorTypes.USER_INPUT, + "You cannot quarantine bots." + ); + } + + await interaction.deferReply(); + + // Quarantine by denying send messages permission across all channels + const guild = interaction.guild; + let channelsModified = 0; + const failedChannels = []; + + for (const [, channel] of guild.channels.cache) { + try { + // Skip DM channels and threads + if (channel.isDMBased() || channel.isThread()) { + continue; + } + + // Skip voice channels + if (channel.type === ChannelType.GuildVoice) { + continue; + } + + // Set deny permissions for sending messages + await channel.permissionOverwrites.edit( + targetUser.id, + { + SendMessages: false, + SendMessagesInThreads: false, + CreatePublicThreads: false, + CreatePrivateThreads: false + }, + `Quarantine: ${reason}` + ); + channelsModified++; + } catch (error) { + logger.warn(`Failed to quarantine user in channel ${channel.name}:`, error); + failedChannels.push(channel.name); + } + } + + // Log the moderation action + const caseId = await logModerationAction({ + client, + guild: interaction.guild, + event: { + action: "User Quarantined", + target: `${targetUser.tag} (${targetUser.id})`, + executor: `${interaction.user.tag} (${interaction.user.id})`, + reason, + metadata: { + userId: targetUser.id, + moderatorId: interaction.user.id, + channelsModified, + failedChannels: failedChannels.length > 0 ? failedChannels.join(', ') : 'none' + } + } + }); + + // Prepare response message + let description = `**Target:** ${targetUser.tag}\n**Reason:** ${reason}\n**Case ID:** #${caseId}\n**Channels Modified:** ${channelsModified}`; + if (failedChannels.length > 0) { + description += `\nāš ļø **Failed to modify:** ${failedChannels.join(', ')}`; + } + + await InteractionHelper.universalReply(interaction, { + embeds: [ + successEmbed( + `šŸ”’ **Quarantined** ${targetUser.tag}`, + description, + ), + ], + }); + + logger.info(`User ${targetUser.tag} quarantined in ${guild.name} by ${interaction.user.tag}`); + } catch (error) { + logger.error('Quarantine user command error:', error); + const errorEmbed_default = errorEmbed( + "An unexpected error occurred while trying to quarantine the user.", + error.message || "Could not quarantine the user" + ); + await InteractionHelper.universalReply(interaction, { embeds: [errorEmbed_default] }); + } + } +}; \ No newline at end of file From c0936149e31f5592206e801952105cbd0eb4c1b9 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 18:28:05 +0700 Subject: [PATCH 64/86] Fix indentation bug in shutdown method - move shutdownLog before process.exit --- src/app.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/app.js b/src/app.js index c0625b6b5a..c199e62e2a 100644 --- a/src/app.js +++ b/src/app.js @@ -1,4 +1,4 @@ -import 'dotenv/config'; +import 'dotenv/config'; import { Client, Collection, GatewayIntentBits } from 'discord.js'; import { REST } from '@discordjs/rest'; import express from 'express'; @@ -346,7 +346,7 @@ class TitanBot extends Client { } logger.info('āœ… Graceful shutdown complete'); - shutdownLog('Bot stopped successfully.'); + shutdownLog('Bot stopped successfully.'); process.exit(0); } catch (error) { logger.error('Error during graceful shutdown:', error); @@ -381,6 +381,3 @@ try { } export default TitanBot; - - - From 6883f417b84bd1b8037f36687193bea0c3eac5c7 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 18:33:42 +0700 Subject: [PATCH 65/86] Add .env file with owner IDs configured --- .env | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000000..cc61343af1 --- /dev/null +++ b/.env @@ -0,0 +1,50 @@ +# Copy this file to .env and replace placeholder values. + +# Discord Bot Configuration +DISCORD_TOKEN=your_discord_bot_token_here +CLIENT_ID=your_discord_client_id_here +GUILD_ID=your_discord_guild_id_here +OWNER_IDS=1398076402460524545,1291778460641136641,1055209717544276040 + +# Bot Runtime Configuration +NODE_ENV=production +LOG_LEVEL=warn +LOG_TO_FILE=false +SENTRY_DSN= + +# Web/API Configuration +PORT=3000 +WEB_HOST=0.0.0.0 +PORT_RETRY_ATTEMPTS=5 +CORS_ORIGIN=* + +# PostgreSQL Configuration (Primary Database) +POSTGRES_URL=postgresql://postgres:yourpassword@localhost:5432/titanbot +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_DB=titanbot +POSTGRES_USER=postgres +POSTGRES_PASSWORD=yourpassword + +# PostgreSQL Pool/Timeout Settings +POSTGRES_MAX_CONNECTIONS=20 +POSTGRES_MIN_CONNECTIONS=2 +POSTGRES_CONNECTION_TIMEOUT=10000 +POSTGRES_IDLE_TIMEOUT=30000 +POSTGRES_RETRIES=3 +POSTGRES_BACKOFF_BASE=100 +POSTGRES_BACKOFF_MULTIPLIER=2 + +# Migration & Schema Settings +AUTO_MIGRATE=true +POSTGRES_MIGRATION_TABLE=schema_migrations +SCHEMA_VERSION=1 +SCHEMA_VERSION_LABEL=baseline-v1 + +# Backup/Restore Script Settings +BACKUP_DIR=./backups +BACKUP_RETENTION_DAYS=14 +POSTGRES_RESTORE_URL= + +# Optional Feature/API Keys +TMDB_API_KEY= From 0adba44372af26ab1ebc532d09c9ad353adf8b2a Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 19:52:23 +0700 Subject: [PATCH 66/86] feat: automatically assign quarantine role when user is quarantined --- src/commands/Moderation/quarantine-user.js | 30 ++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/commands/Moderation/quarantine-user.js b/src/commands/Moderation/quarantine-user.js index d8eae07bf6..485018e2a3 100644 --- a/src/commands/Moderation/quarantine-user.js +++ b/src/commands/Moderation/quarantine-user.js @@ -70,6 +70,8 @@ export default { const guild = interaction.guild; let channelsModified = 0; const failedChannels = []; + let roleAssigned = false; + let roleAssignmentError = null; for (const [, channel] of guild.channels.cache) { try { @@ -101,6 +103,23 @@ export default { } } + // Attempt to assign the quarantine role + const QUARANTINE_ROLE_ID = "1516423888605675650"; + try { + const quarantineRole = guild.roles.cache.get(QUARANTINE_ROLE_ID); + if (quarantineRole) { + await member.roles.add(quarantineRole, `Quarantine: ${reason}`); + roleAssigned = true; + logger.info(`Assigned quarantine role to ${targetUser.tag}`); + } else { + roleAssignmentError = "Quarantine role not found in server"; + logger.warn(`Quarantine role ${QUARANTINE_ROLE_ID} not found in guild ${guild.id}`); + } + } catch (error) { + roleAssignmentError = error.message; + logger.warn(`Failed to assign quarantine role to ${targetUser.tag}:`, error); + } + // Log the moderation action const caseId = await logModerationAction({ client, @@ -114,13 +133,20 @@ export default { userId: targetUser.id, moderatorId: interaction.user.id, channelsModified, - failedChannels: failedChannels.length > 0 ? failedChannels.join(', ') : 'none' + failedChannels: failedChannels.length > 0 ? failedChannels.join(', ') : 'none', + roleAssigned, + roleAssignmentError: roleAssignmentError || 'none' } } }); // Prepare response message let description = `**Target:** ${targetUser.tag}\n**Reason:** ${reason}\n**Case ID:** #${caseId}\n**Channels Modified:** ${channelsModified}`; + if (roleAssigned) { + description += `\nāœ… **Quarantine Role:** Assigned`; + } else if (roleAssignmentError) { + description += `\nāš ļø **Quarantine Role:** Failed to assign (${roleAssignmentError})`; + } if (failedChannels.length > 0) { description += `\nāš ļø **Failed to modify:** ${failedChannels.join(', ')}`; } @@ -144,4 +170,4 @@ export default { await InteractionHelper.universalReply(interaction, { embeds: [errorEmbed_default] }); } } -}; \ No newline at end of file +}; From c05c6e05a63400e1b19c4530e790b9d5a7cf3fe8 Mon Sep 17 00:00:00 2001 From: arand606 Date: Tue, 16 Jun 2026 20:02:20 +0700 Subject: [PATCH 67/86] Update unquarantine-user.js --- src/commands/Moderation/unquarantine-user.js | 37 +++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/commands/Moderation/unquarantine-user.js b/src/commands/Moderation/unquarantine-user.js index 2d48b903cc..03cf062b22 100644 --- a/src/commands/Moderation/unquarantine-user.js +++ b/src/commands/Moderation/unquarantine-user.js @@ -52,6 +52,8 @@ export default { const guild = interaction.guild; let channelsModified = 0; const failedChannels = []; + let roleRemoved = false; + let roleRemovalError = null; for (const [, channel] of guild.channels.cache) { try { @@ -74,6 +76,27 @@ export default { } } + // Attempt to remove the quarantine role + const QUARANTINE_ROLE_ID = "1516423888605675650"; + try { + const quarantineRole = guild.roles.cache.get(QUARANTINE_ROLE_ID); + if (quarantineRole) { + if (member.roles.cache.has(QUARANTINE_ROLE_ID)) { + await member.roles.remove(quarantineRole, `Unquarantine: ${reason}`); + roleRemoved = true; + logger.info(`Removed quarantine role from ${targetUser.tag}`); + } else { + roleRemoved = false; // User didn't have the role to begin with + } + } else { + roleRemovalError = "Quarantine role not found in server"; + logger.warn(`Quarantine role ${QUARANTINE_ROLE_ID} not found in guild ${guild.id}`); + } + } catch (error) { + roleRemovalError = error.message; + logger.warn(`Failed to remove quarantine role from ${targetUser.tag}:`, error); + } + // Log the moderation action const caseId = await logModerationAction({ client, @@ -87,13 +110,24 @@ export default { userId: targetUser.id, moderatorId: interaction.user.id, channelsModified, - failedChannels: failedChannels.length > 0 ? failedChannels.join(', ') : 'none' + failedChannels: failedChannels.length > 0 ? failedChannels.join(', ') : 'none', + roleRemoved, + roleRemovalError: roleRemovalError || 'none' } } }); // Prepare response message let description = `**Reason:** ${reason}\n**Case ID:** #${caseId}\n**Channels Modified:** ${channelsModified}`; + + if (roleRemoved) { + description += `\nāœ… **Quarantine Role:** Removed`; + } else if (roleRemovalError) { + description += `\nāš ļø **Quarantine Role:** Failed to remove (${roleRemovalError})`; + } else { + description += `\nā„¹ļø **Quarantine Role:** User did not have the role`; + } + if (failedChannels.length > 0) { description += `\nāš ļø **Failed to modify:** ${failedChannels.join(', ')}`; } @@ -118,3 +152,4 @@ export default { } } }; + From c47e5d0c7707acc93a7f2f1feec15351e6f7c3f1 Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 17 Jun 2026 12:05:38 +0700 Subject: [PATCH 68/86] Update quarantine-user.js From a8be3f3816731e17a2f150e992d08a8d1634953a Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 17 Jun 2026 12:06:26 +0700 Subject: [PATCH 69/86] Update quarantine-user.js From 7fd78b58a7615c0cafae2c2d0bb5834056ecd9f0 Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 17 Jun 2026 12:07:26 +0700 Subject: [PATCH 70/86] Update unquarantine-user.js From 23502d253ab5c5a6a0533c0d78b0de30fc963ace Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 19 Jun 2026 07:00:23 +0700 Subject: [PATCH 71/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 97b8978a56..df42a74478 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -30,7 +30,7 @@ export const botConfig = { type: 0, // 0 = Playing }, { - name: "OWO Please join M00NK1DD~ UWU Nya~ 🄺", + name: "Syntax Errors", type: 2, // 2 = Listening }, { From c72dd94688be93a9b97fabab40ade9d161cfff7f Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 19 Jun 2026 11:48:18 +0700 Subject: [PATCH 72/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index df42a74478..2176d9d102 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -172,7 +172,7 @@ export const botConfig = { // Plural display name. namePlural: "coins", // Currency symbol shown in balances. - symbol: "$", + symbol: "€", }, // Starting balance for new users. From ea830b9f70633c3f1a7100179009ced3df3f9711 Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 19 Jun 2026 11:53:17 +0700 Subject: [PATCH 73/86] Update bot.js --- src/config/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/bot.js b/src/config/bot.js index 2176d9d102..622dd4900a 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -168,9 +168,9 @@ export const botConfig = { economy: { currency: { // Currency display name. - name: "coins", + name: "euro", // Plural display name. - namePlural: "coins", + namePlural: "euro", // Currency symbol shown in balances. symbol: "€", }, From 595e677cc186e807f06d0402703d36d0766a7443 Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 24 Jun 2026 16:31:50 +0700 Subject: [PATCH 74/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 622dd4900a..5f028223aa 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 12000, + cycleInterval: 11000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From 3a5eae46be3b3068d2e7e3409e9e90121c513a6f Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 24 Jun 2026 16:32:27 +0700 Subject: [PATCH 75/86] Update ready.js --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index ead60ef7ba..098c7eb6fb 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -30,7 +30,7 @@ export default { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 12000); + }, cycleInterval || 11000); } else { // Fallback if no activities configured client.user.setPresence(config.bot.presence); From ce7c94a26ca946bcf951ca7c621f547c6c1e4182 Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 1 Jul 2026 14:47:13 +0700 Subject: [PATCH 76/86] Add comprehensive bug analysis report --- BUG_ANALYSIS.md | 706 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 706 insertions(+) create mode 100644 BUG_ANALYSIS.md diff --git a/BUG_ANALYSIS.md b/BUG_ANALYSIS.md new file mode 100644 index 0000000000..58fa86906b --- /dev/null +++ b/BUG_ANALYSIS.md @@ -0,0 +1,706 @@ +# šŸ› Bug Analysis Report - M00NK1DD-B0T_ + +**Generated:** 2026-07-01 +**Repository:** arand606/M00NK1DD-B0T_ +**Language:** JavaScript (100%) + +--- + +## Executive Summary + +This document outlines **critical issues**, **bugs**, and **code quality concerns** found in the M00NK1DD-B0T_ codebase. The analysis covers the main application flow, database abstraction, error handling, and utility functions. Issues are categorized by severity. + +--- + +## šŸ”“ CRITICAL ISSUES + +### 1. **Unsafe CORS Configuration** +**File:** `src/app.js` (Lines 113-127) +**Severity:** HIGH +**Impact:** Security vulnerability; potential for CORS bypass + +```javascript +// āŒ PROBLEMATIC CODE +const allowedOrigins = Array.isArray(corsOrigin) ? corsOrigin : [corsOrigin]; +const origin = req.headers.origin; + +if (allowedOrigins.includes('*') || allowedOrigins.includes(origin)) { + res.header('Access-Control-Allow-Origin', origin || '*'); +} +``` + +**Problems:** +- When `corsOrigin` is `'*'`, the condition checks if an array contains the string `'*'` +- Setting `Access-Control-Allow-Origin: *` with credentials enabled violates CORS spec +- The wildcard `'*'` in `allowedOrigins` array is treated as a literal string, not a pattern match + +**Fix:** +```javascript +const allowedOrigins = Array.isArray(corsOrigin) ? corsOrigin : [corsOrigin]; +const origin = req.headers.origin; +const hasWildcard = allowedOrigins.includes('*'); + +if (hasWildcard) { + res.header('Access-Control-Allow-Origin', '*'); +} else if (allowedOrigins.includes(origin)) { + res.header('Access-Control-Allow-Origin', origin); +} +``` + +--- + +### 2. **Memory Leak in Rate Limiter** +**File:** `src/app.js` (Lines 129-151) +**Severity:** HIGH +**Impact:** Memory exhaustion under sustained load + +```javascript +// āŒ PROBLEMATIC CODE +const requestCounts = new Map(); +app.use((req, res, next) => { + const ip = req.ip; + const now = Date.now(); + const windowStart = now - windowMs; + + if (!requestCounts.has(ip)) { + requestCounts.set(ip, []); + } + + const times = requestCounts.get(ip).filter(t => t > windowStart); + times.push(now); + requestCounts.set(ip, times); // āŒ OLD ENTRIES ACCUMULATE + next(); +}); +``` + +**Problems:** +- IPs with `times.length === 0` remain as keys in the Map with empty arrays +- Over time with many unique IPs, the Map grows unboundedly +- No garbage collection mechanism for old or stale entries + +**Fix:** +```javascript +const requestCounts = new Map(); +app.use((req, res, next) => { + const ip = req.ip; + const now = Date.now(); + const windowStart = now - windowMs; + + let times = requestCounts.get(ip) || []; + times = times.filter(t => t > windowStart); + + if (times.length >= maxRequests) { + return res.status(429).json({ error: 'Too many requests' }); + } + + times.push(now); + + // Only store if times exist, otherwise cleanup + if (times.length > 0) { + requestCounts.set(ip, times); + } else { + requestCounts.delete(ip); + } + + next(); +}); +``` + +--- + +### 3. **Type Error in Handler Loader** +**File:** `src/app.js` (Lines 281-283) +**Severity:** MEDIUM-HIGH +**Impact:** Runtime error when handler type contains colon + +```javascript +// āŒ PROBLEMATIC CODE +const loaderFn = handler.type.startsWith('named:') + ? module[handler.type.split(':')[1]] // āŒ UNDEFINED if no colon or no [1] + : module.default; +``` + +**Problems:** +- `handler.type.split(':')` may return array with only one element +- Accessing `[1]` on a single-element array returns `undefined` +- No validation that the split actually produced a named export +- If `handler.type` is `'named'` (missing colon), `split(':')` returns `['named']` and `[1]` is undefined + +**Example fail case:** +```javascript +const handler = { path: 'events', type: 'named', required: true }; +// handler.type.startsWith('named:') === false +// Falls through to module.default āœ“ OK +// BUT if type is 'named:myExport': +// handler.type.split(':')[1] === 'myExport' āœ“ +// BUT if split returns only 1 element: +// module[undefined] throws or returns undefined āŒ +``` + +**Fix:** +```javascript +let loaderFn; +if (handler.type.startsWith('named:')) { + const parts = handler.type.split(':'); + if (parts.length < 2 || !parts[1]) { + throw new Error(`Invalid handler type format: "${handler.type}". Expected "named:exportName"`); + } + loaderFn = module[parts[1]]; + if (!loaderFn) { + throw new Error(`Export "${parts[1]}" not found in ${handler.path}`); + } +} else { + loaderFn = module.default; +} +``` + +--- + +## 🟠 HIGH-PRIORITY ISSUES + +### 4. **Unvalidated Port Binding Errors** +**File:** `src/app.js` (Lines 203-224) +**Severity:** MEDIUM +**Impact:** Fails silently on permission errors + +```javascript +// āŒ PROBLEMATIC CODE +server.on('error', (error) => { + const errorCode = error?.code || 'UNKNOWN_ERROR'; + + if (!hasStartedListening && errorCode === 'EADDRINUSE' && attempt < maxPortRetryAttempts) { + // Retry logic āœ“ + } + + if (hasStartedListening && errorCode === 'EADDRINUSE') { + logger.warn('...duplicate bind warning...'); + return; // āŒ Silently returns on port conflict AFTER binding + } + + logger.error(`Web server error: ${errorMessage}`); + // āŒ No handling for EACCES, EPERM, or other OS errors +}); +``` + +**Problems:** +- Only handles `EADDRINUSE` and `UNKNOWN_ERROR` +- Doesn't handle `EACCES` (permission denied), `EPERM` (operation not permitted) +- Silently returns if server started but port becomes unavailable +- No retry for permission-related errors + +**Likely scenarios:** +- Port < 1024 requires root/admin +- Firewall blocking port +- Previous process still holding port + +**Fix:** +```javascript +const OS_ERRORS = { + 'EADDRINUSE': 'Port is already in use', + 'EACCES': 'Permission denied (port < 1024 requires elevated privileges)', + 'EPERM': 'Operation not permitted', + 'ENOTFOUND': 'Host not found', + 'EHOSTUNREACH': 'Host is unreachable', +}; + +server.on('error', (error) => { + const errorCode = error?.code || 'UNKNOWN_ERROR'; + const errorMsg = OS_ERRORS[errorCode] || error.message; + + if (!hasStartedListening && errorCode === 'EADDRINUSE' && attempt < maxPortRetryAttempts) { + startupLog(`Port ${port} in use. Retrying on ${port + 1}...`); + setTimeout(() => startServer(port + 1, attempt + 1), 250); + return; + } + + if (!hasStartedListening && errorCode === 'EACCES') { + logger.error(`āŒ Cannot bind to port ${port}: ${errorMsg}`); + process.exit(1); + } + + logger.error(`āŒ Web server error (${errorCode}): ${errorMsg}`); + if (!hasStartedListening) process.exit(1); +}); +``` + +--- + +### 5. **Race Condition in Counter Updates** +**File:** `src/app.js` (Lines 236-270) +**Severity:** MEDIUM +**Impact:** Data inconsistency; orphaned counters + +```javascript +// āš ļø PROBLEMATIC CODE - Race condition possible +for (const [guildId, guild] of this.guilds.cache) { + const counters = await getServerCounters(this, guildId); + + for (const counter of counters) { + const channel = guild.channels.cache.get(counter.channelId); + if (channel) { + // Counter is valid + } else { + // Channel deleted, but what if it's recreated between checks? + orphanedCounters.push(counter); + } + } + + if (orphanedCounters.length > 0) { + await saveServerCounters(this, guildId, validCounters); + } +} +``` + +**Problems:** +- Between `getServerCounters()` and `saveServerCounters()`, the guild state can change +- A channel may be deleted and recreated +- Multiple cron jobs could run simultaneously, causing conflicts + +**Fix:** Use mutex or transaction-like behavior (the codebase has `Mutex` at `src/utils/mutex.js`): +```javascript +import { Mutex } from './utils/mutex.js'; + +async updateAllCounters() { + for (const [guildId, guild] of this.guilds.cache) { + // Lock per guild to prevent concurrent updates + await Mutex.runExclusive(`counter:${guildId}`, async () => { + const counters = await getServerCounters(this, guildId); + const validCounters = []; + + for (const counter of counters) { + if (counter?.type && counter?.channelId && counter.enabled !== false) { + const channel = guild.channels.cache.get(counter.channelId); + if (channel) { + validCounters.push(counter); + await updateCounter(this, guild, counter); + } + } + } + + await saveServerCounters(this, guildId, validCounters); + }); + } +} +``` + +--- + +### 6. **Database Status Check Ambiguity** +**File:** `src/app.js` (Lines 154-183) +**Severity:** MEDIUM +**Impact:** Misleading health checks + +```javascript +// āŒ PROBLEMATIC CODE +app.get('/ready', (req, res) => { + const dbStatus = this.db?.getStatus?.() || { isDegraded: true }; // āš ļø Defaults to degraded + const isReady = this.isReady() && !dbStatus.isDegraded; + + if (isReady) { + return res.status(200).json({ ready: true, message: 'Bot is ready' }); + } + + res.status(503).json({ + ready: false, + reason: !this.isReady() ? 'Bot not Ready' : 'Database degraded' + }); +}); +``` + +**Problems:** +- Defaults to `{ isDegraded: true }` if `this.db` is undefined +- This causes `/ready` to return 503 even if bot is actually initializing normally +- No distinction between "database missing" and "database degraded" +- Unclear whether this is during startup (expected) or runtime (problem) + +**Fix:** +```javascript +app.get('/ready', (req, res) => { + if (!this.db) { + return res.status(503).json({ + ready: false, + reason: 'Database not initialized' + }); + } + + const dbStatus = this.db.getStatus(); + const isReady = this.isReady() && !dbStatus.isDegraded; + + if (isReady) { + return res.status(200).json({ + ready: true, + message: 'Bot is ready', + database: dbStatus.connectionType + }); + } + + res.status(503).json({ + ready: false, + reason: !this.isReady() ? 'Bot not ready' : 'Database degraded', + database: dbStatus + }); +}); +``` + +--- + +## 🟔 MEDIUM-PRIORITY ISSUES + +### 7. **Missing Null Checks in Database Wrapper** +**File:** `src/utils/database.js` (Lines 105-112) +**Severity:** MEDIUM +**Impact:** Potential runtime errors + +```javascript +// āš ļø PROBLEMATIC CODE +async increment(key, amount = 1) { + if (this.db.increment) { // āŒ No null check on this.db + return this.db.increment(key, amount); + } + const current = await this.db.get(key, 0); // āŒ What if this.db is null? + const newValue = current + amount; + await this.db.set(key, newValue); + return newValue; +} +``` + +**Problems:** +- If `this.db` is null, `this.db.increment` throws +- Multiple methods lack null checks on `this.db` +- No guard clause at method entry + +**Fix:** +```javascript +async increment(key, amount = 1) { + if (!this.db) { + throw new Error('Database not initialized'); + } + + if (typeof this.db.increment === 'function') { + return this.db.increment(key, amount); + } + + const current = await this.db.get(key, 0); + const newValue = current + amount; + await this.db.set(key, newValue); + return newValue; +} +``` + +--- + +### 8. **Improper Error Propagation in Event Handler** +**File:** `src/handlers/events.js` (Lines 16-23) +**Severity:** MEDIUM +**Impact:** Silent event failures + +```javascript +// āš ļø PROBLEMATIC CODE +const safeExecute = async (...args) => { + try { + await event.execute(...args, client); + } catch (error) { + logger.error(`Error executing event ${event.name}:`, error); + // āŒ Error swallowed, not re-thrown or handled further + } +}; +``` + +**Problems:** +- Events that fail are logged but not tracked +- No way to know if critical events failed +- No circuit breaker or fallback + +**Better approach:** +```javascript +const safeExecute = async (...args) => { + try { + await event.execute(...args, client); + } catch (error) { + logger.error(`Error executing event ${event.name}:`, error); + + // For critical events, consider re-throwing + const criticalEvents = new Set(['ready', 'guildCreate', 'guildDelete']); + if (criticalEvents.has(event.name) && client.skipCriticalEventErrors !== true) { + throw error; // Re-throw to halt startup + } + } +}; +``` + +--- + +### 9. **Unvalidated Application Status Values** +**File:** `src/utils/database.js` (Lines 1268-1276) +**Severity:** MEDIUM +**Impact:** Data corruption; invalid states + +```javascript +// āš ļø PROBLEMATIC CODE +const status = typeof application.status === 'string' ? application.status.toLowerCase() : 'pending'; + +if (status === 'pending') { + return ageMsFromCreated > pendingRetentionMs; +} + +if (status === 'approved' || status === 'denied') { // āš ļø What about other values? + return ageMsFromReviewed > reviewedRetentionMs; +} + +return ageMsFromCreated > pendingRetentionMs; // āš ļø Default fallback +``` + +**Problems:** +- Accepts any string as status (no validation) +- No enum or constant for valid statuses +- Database could contain `status: 'invalid'` or `status: 'PENDING'` (uppercase) +- Default behavior may not be what's intended + +**Fix:** +```javascript +const VALID_STATUSES = ['pending', 'approved', 'denied', 'rejected']; + +function isApplicationExpired(application, retentionDays, now = Date.now()) { + if (!application || typeof application !== 'object') { + return false; + } + + const rawStatus = application.status; + const status = typeof rawStatus === 'string' + ? rawStatus.toLowerCase().trim() + : 'pending'; + + if (!VALID_STATUSES.includes(status)) { + logger.warn(`Invalid application status: "${rawStatus}". Treating as pending.`); + } + + // ... rest of logic +} +``` + +--- + +### 10. **Missing Request Timeout Configuration** +**File:** `src/app.js` (Lines 106-228) +**Severity:** MEDIUM +**Impact:** Hanging requests; resource exhaustion + +```javascript +// āš ļø INCOMPLETE CODE +const app = express(); +// āŒ No timeout configuration +app.use((req, res, next) => { + // Rate limiting, CORS, etc. + next(); +}); + +const server = app.listen(port, host, () => { + // āŒ No keepAliveTimeout or requestTimeout +}); +``` + +**Problems:** +- Requests can hang indefinitely +- Slow clients can hold connections open +- No protection against slowloris attacks + +**Fix:** +```javascript +const app = express(); + +// Add timeout middleware +app.use((req, res, next) => { + req.setTimeout(30000); // 30 seconds per request + res.setTimeout(30000); + next(); +}); + +// ... other middleware + +const server = app.listen(port, host, () => { + server.keepAliveTimeout = 65000; + server.requestTimeout = 60000; + startupLog(`āœ… Web Server running on ${host}:${port}`); +}); +``` + +--- + +## 🟢 LOW-PRIORITY ISSUES + +### 11. **Redundant Console Transport in Logger** +**File:** `src/utils/logger.js` (Lines 154-174) +**Severity:** LOW +**Impact:** Code duplication + +```javascript +// āš ļø REDUNDANT CODE +if (process.env.NODE_ENV !== 'production') { + logger.add(new transports.Console({ + format: combine(colorize(), timestamp({ format: 'HH:mm:ss' }), errors({ stack: true }), logFormat), + level: resolvedLogLevel, + })); +} else { + logger.add(new transports.Console({ // āŒ EXACT SAME CONFIG + format: combine(colorize(), timestamp({ format: 'HH:mm:ss' }), errors({ stack: true }), logFormat), + level: resolvedLogLevel, + })); +} +``` + +**Fix:** +```javascript +const consoleTransport = new transports.Console({ + format: combine(colorize(), timestamp({ format: 'HH:mm:ss' }), errors({ stack: true }), logFormat), + level: resolvedLogLevel, +}); +logger.add(consoleTransport); +``` + +--- + +### 12. **Excessive Modlog Settings Duplication** +**File:** `src/utils/database.js` (Lines 1656-1789) +**Severity:** LOW +**Impact:** Maintenance burden; inconsistency risk + +```javascript +// āš ļø PROBLEMATIC PATTERN +const defaultSettings = { + logBans: true, + logKicks: true, + logMutes: true, + // ... 50+ similar boolean flags ... + logGuildScheduledEventUsersUpdate: true, +}; + +// Repeated again in error case: +return { + logBans: true, + logKicks: true, + // ... ALL REPEATED ... +}; +``` + +**Problems:** +- Defaults defined in two places +- Hard to maintain consistency +- Prone to drift between versions + +**Fix:** +```javascript +const DEFAULT_MODLOG_SETTINGS = { + enabled: false, + channelId: null, + ignoredChannels: [], + ignoredUsers: [], + ignoredActions: [], + logActions: { + bans: true, + kicks: true, + mutes: true, + warns: true, + // ... + } +}; + +export async function getModlogSettings(client, guildId) { + const key = getModlogSettingsKey(guildId); + try { + const settings = await client.db.get(key, {}); + return { ...DEFAULT_MODLOG_SETTINGS, ...unwrapReplitData(settings) }; + } catch (error) { + logger.error(`Error getting modlog settings for guild ${guildId}:`, error); + return DEFAULT_MODLOG_SETTINGS; + } +} +``` + +--- + +### 13. **Hardcoded String Values for Logging** +**File:** `src/app.js` (Various lines) +**Severity:** LOW +**Impact:** Inconsistent log messages; hard to search/standardize + +```javascript +// āš ļø SCATTERED STRINGS +logger.info('āœ… Database Status: ...'); +startupLog('Starting TitanBot...'); +logger.error('āŒ Database Initialization Error:', error); +shutdownLog('Bot stopped successfully.'); +``` + +**Recommendation:** Extract to constants: +```javascript +const LOG_MESSAGES = { + DATABASE_INIT_START: 'Initializing database...', + DATABASE_INIT_SUCCESS: 'āœ… Database initialized', + DATABASE_INIT_ERROR: 'āŒ Database Initialization Error:', + BOT_STARTUP: 'Starting TitanBot...', + BOT_ONLINE: 'ONLINE āœ…', + BOT_SHUTDOWN: 'Bot is shutting down', +}; +``` + +--- + +## šŸ“‹ Summary Table + +| Issue | File | Severity | Type | Recommendation | +|-------|------|----------|------|-----------------| +| Unsafe CORS | app.js | šŸ”“ CRITICAL | Security | Fix wildcard handling | +| Rate Limiter Memory Leak | app.js | šŸ”“ CRITICAL | Performance | Add cleanup logic | +| Handler Type Error | app.js | 🟠 HIGH | Runtime | Add validation | +| Port Binding Errors | app.js | 🟠 HIGH | Reliability | Handle OS errors | +| Counter Race Condition | app.js | 🟠 HIGH | Data Integrity | Use Mutex | +| DB Status Ambiguity | app.js | 🟠 HIGH | Observability | Clarify states | +| Missing Null Checks | database.js | 🟔 MEDIUM | Runtime | Add guards | +| Event Error Swallowing | events.js | 🟔 MEDIUM | Debugging | Improve tracking | +| Invalid Status Values | database.js | 🟔 MEDIUM | Data Integrity | Add enum validation | +| Missing Timeouts | app.js | 🟔 MEDIUM | Security | Configure limits | +| Redundant Logger Config | logger.js | 🟢 LOW | Quality | DRY principle | +| Modlog Settings Duplication | database.js | 🟢 LOW | Maintainability | Extract constant | +| Hardcoded Strings | app.js | 🟢 LOW | Standards | Use constants | + +--- + +## āœ… Recommendations + +1. **Immediate (This week):** + - [ ] Fix CORS configuration (Issue #1) + - [ ] Implement rate limiter cleanup (Issue #2) + - [ ] Add handler type validation (Issue #3) + +2. **Short-term (This sprint):** + - [ ] Enhance port binding error handling (Issue #4) + - [ ] Add mutex-based counter updates (Issue #5) + - [ ] Clarify database status checks (Issue #6) + +3. **Medium-term (Next sprint):** + - [ ] Add comprehensive null checks (Issue #7) + - [ ] Improve event error handling (Issue #8) + - [ ] Add status enum validation (Issue #9) + +4. **Long-term (Ongoing):** + - [ ] Add request timeouts (Issue #10) + - [ ] Refactor for DRY principles (Issues #11-13) + - [ ] Add integration tests + - [ ] Implement request/response middleware logging + +--- + +## šŸ“š Related Files for Reference + +- **Main entry:** `src/app.js` +- **Database:** `src/utils/database.js` +- **Logger:** `src/utils/logger.js` +- **Event handlers:** `src/handlers/events.js` +- **Error handling:** `src/utils/errorHandler.js` +- **Mutex utility:** `src/utils/mutex.js` (already implements locking!) + +--- + +**Report prepared:** 2026-07-01 +**Status:** Initial Assessment +**Next Review:** After critical issues are resolved From b3ad5a2f573f1aabbbfaf579ded60d8b984304ad Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 1 Jul 2026 15:18:01 +0700 Subject: [PATCH 77/86] Add announce slash command (utility) --- src/commands/Utility/announce.js | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/commands/Utility/announce.js diff --git a/src/commands/Utility/announce.js b/src/commands/Utility/announce.js new file mode 100644 index 0000000000..242f0eb6ea --- /dev/null +++ b/src/commands/Utility/announce.js @@ -0,0 +1,114 @@ +import { SlashCommandBuilder, PermissionFlagsBits, ChannelType } from 'discord.js'; +import { createEmbed, errorEmbed, successEmbed } from '../../utils/embeds.js'; +import { InteractionHelper } from '../../utils/interactionHelper.js'; +import { logger } from '../../utils/logger.js'; + +export default { + data: new SlashCommandBuilder() + .setName('announce') + .setDescription('Make the bot send an announcement message. (Manage Server required)') + .addChannelOption(option => + option + .setName('channel') + .setDescription('Channel to send the announcement in. Defaults to the current channel.') + .addChannelTypes(ChannelType.GuildText, ChannelType.GuildAnnouncement) + .setRequired(false) + ) + .addStringOption(option => + option + .setName('message') + .setDescription('The announcement message to send') + .setRequired(true) + ) + .addRoleOption(option => + option + .setName('role') + .setDescription('Optional role to mention in the announcement') + .setRequired(false) + ) + .addBooleanOption(option => + option + .setName('everyone') + .setDescription('If true, mention @everyone in the announcement') + .setRequired(false) + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), + + category: 'utility', + + async execute(interaction, config, client) { + const deferSuccess = await InteractionHelper.safeDefer(interaction); + if (!deferSuccess) { + logger.warn(`Announce interaction defer failed`, { + userId: interaction.user.id, + guildId: interaction.guildId, + commandName: 'announce' + }); + return; + } + + try { + if (!interaction.member.permissions.has(PermissionFlagsBits.ManageGuild)) { + return await InteractionHelper.safeEditReply(interaction, { + embeds: [errorEmbed('Permission Denied', 'You need the **Manage Server** permission to use this command.')], + flags: 1 << 6, // Ephemeral + }); + } + + const targetChannel = interaction.options.getChannel('channel') || interaction.channel; + const messageText = interaction.options.getString('message').trim(); + const pingRole = interaction.options.getRole('role'); + const mentionEveryone = interaction.options.getBoolean('everyone'); + + if (!targetChannel || !targetChannel.isTextBased()) { + return await InteractionHelper.safeEditReply(interaction, { + embeds: [errorEmbed('Invalid Channel', 'Please provide a valid text channel to send the announcement in.')], + flags: 1 << 6, + }); + } + + const botMember = interaction.guild.members.me; + const botPerms = targetChannel.permissionsFor(botMember); + if (!botPerms || !botPerms.has([PermissionFlagsBits.SendMessages])) { + return await InteractionHelper.safeEditReply(interaction, { + embeds: [errorEmbed('Missing Permission', `I don't have permission to send messages in ${targetChannel}.`)], + flags: 1 << 6, + }); + } + + // Build the announcement content + let prefix = ''; + if (pingRole) { + prefix = `<@&${pingRole.id}> `; + } else if (mentionEveryone) { + // Check bot has mention everyone permission + if (!botPerms.has(PermissionFlagsBits.MentionEveryone)) { + return await InteractionHelper.safeEditReply(interaction, { + embeds: [errorEmbed('Missing Permission', "I don't have permission to mention @everyone in that channel.")], + flags: 1 << 6, + }); + } + prefix = '@everyone '; + } + + // Send the announcement + const sentMessage = await targetChannel.send({ content: `${prefix}${messageText}` }); + + const success = successEmbed('Announcement Sent', `Your announcement was sent in ${targetChannel}.`); + success.addFields ? success.addFields({ name: 'Link', value: `[Jump to message](${sentMessage.url})` }) : null; + + await InteractionHelper.safeEditReply(interaction, { + embeds: [success], + flags: 1 << 6, + }); + + logger.info(`Announcement sent by ${interaction.user.tag} in ${interaction.guildId} -> ${targetChannel.id}`); + } catch (error) { + logger.error('Announce command error:', error); + await InteractionHelper.safeEditReply(interaction, { + embeds: [errorEmbed('Failed to Send', 'An unexpected error occurred while sending the announcement.')], + flags: 1 << 6, + }); + } + } +}; From 2aa14e9eeaceb04efb30e98d66aff544b0a5e19e Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 1 Jul 2026 15:27:43 +0700 Subject: [PATCH 78/86] chore(announce): add safer permission checks, allowedMentions, crosspost handling, and minor robustness improvements --- src/commands/Utility/announce.js | 56 ++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/commands/Utility/announce.js b/src/commands/Utility/announce.js index 242f0eb6ea..f62010dca1 100644 --- a/src/commands/Utility/announce.js +++ b/src/commands/Utility/announce.js @@ -3,6 +3,8 @@ import { createEmbed, errorEmbed, successEmbed } from '../../utils/embeds.js'; import { InteractionHelper } from '../../utils/interactionHelper.js'; import { logger } from '../../utils/logger.js'; +const EPHEMERAL = 1 << 6; + export default { data: new SlashCommandBuilder() .setName('announce') @@ -48,58 +50,84 @@ export default { } try { - if (!interaction.member.permissions.has(PermissionFlagsBits.ManageGuild)) { + // Safer member permission handling across interaction contexts + const memberPerms = interaction.memberPermissions ?? interaction.member?.permissions; + if (!memberPerms?.has(PermissionFlagsBits.ManageGuild)) { return await InteractionHelper.safeEditReply(interaction, { embeds: [errorEmbed('Permission Denied', 'You need the **Manage Server** permission to use this command.')], - flags: 1 << 6, // Ephemeral + flags: EPHEMERAL, }); } const targetChannel = interaction.options.getChannel('channel') || interaction.channel; - const messageText = interaction.options.getString('message').trim(); + const rawMessage = interaction.options.getString('message'); + const messageText = rawMessage ? rawMessage.trim() : ''; const pingRole = interaction.options.getRole('role'); const mentionEveryone = interaction.options.getBoolean('everyone'); + if (!messageText) { + return await InteractionHelper.safeEditReply(interaction, { + embeds: [errorEmbed('Invalid Message', 'Please provide a non-empty announcement message.')], + flags: EPHEMERAL, + }); + } + if (!targetChannel || !targetChannel.isTextBased()) { return await InteractionHelper.safeEditReply(interaction, { embeds: [errorEmbed('Invalid Channel', 'Please provide a valid text channel to send the announcement in.')], - flags: 1 << 6, + flags: EPHEMERAL, }); } - const botMember = interaction.guild.members.me; - const botPerms = targetChannel.permissionsFor(botMember); - if (!botPerms || !botPerms.has([PermissionFlagsBits.SendMessages])) { + const botMember = interaction.guild?.members?.me ?? null; + const botPerms = botMember ? targetChannel.permissionsFor(botMember) : targetChannel.permissionsFor(client.user?.id); + if (!botPerms?.has(PermissionFlagsBits.SendMessages)) { return await InteractionHelper.safeEditReply(interaction, { embeds: [errorEmbed('Missing Permission', `I don't have permission to send messages in ${targetChannel}.`)], - flags: 1 << 6, + flags: EPHEMERAL, }); } - // Build the announcement content + // Build the announcement content and allowedMentions safely let prefix = ''; + const allowedMentions = { parse: [] }; + if (pingRole) { prefix = `<@&${pingRole.id}> `; + // allow only that role to be mentioned (prevents accidental pings) + allowedMentions.roles = [pingRole.id]; } else if (mentionEveryone) { // Check bot has mention everyone permission if (!botPerms.has(PermissionFlagsBits.MentionEveryone)) { return await InteractionHelper.safeEditReply(interaction, { embeds: [errorEmbed('Missing Permission', "I don't have permission to mention @everyone in that channel.")], - flags: 1 << 6, + flags: EPHEMERAL, }); } prefix = '@everyone '; + allowedMentions.parse = ['everyone']; } // Send the announcement - const sentMessage = await targetChannel.send({ content: `${prefix}${messageText}` }); + const sentMessage = await targetChannel.send({ content: `${prefix}${messageText}`, allowedMentions }); + + // If the channel is a News/Announcement channel, try to crosspost so followers receive it + if (targetChannel.type === ChannelType.GuildAnnouncement && typeof sentMessage.crosspost === 'function') { + try { + await sentMessage.crosspost(); + } catch (e) { + logger.warn('Failed to crosspost announcement:', e); + } + } const success = successEmbed('Announcement Sent', `Your announcement was sent in ${targetChannel}.`); - success.addFields ? success.addFields({ name: 'Link', value: `[Jump to message](${sentMessage.url})` }) : null; + if (typeof success.addFields === 'function') { + success.addFields({ name: 'Link', value: `[Jump to message](${sentMessage.url})` }); + } await InteractionHelper.safeEditReply(interaction, { embeds: [success], - flags: 1 << 6, + flags: EPHEMERAL, }); logger.info(`Announcement sent by ${interaction.user.tag} in ${interaction.guildId} -> ${targetChannel.id}`); @@ -107,7 +135,7 @@ export default { logger.error('Announce command error:', error); await InteractionHelper.safeEditReply(interaction, { embeds: [errorEmbed('Failed to Send', 'An unexpected error occurred while sending the announcement.')], - flags: 1 << 6, + flags: EPHEMERAL, }); } } From c824a3f779fc1996e67e948b4f3c3b68bbb7a73e Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 1 Jul 2026 15:50:05 +0700 Subject: [PATCH 79/86] fix(voiceStateUpdate): remove duplicate channelName declaration to prevent SyntaxError --- src/events/voiceStateUpdate.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/events/voiceStateUpdate.js b/src/events/voiceStateUpdate.js index 6753e76fa7..aef8abe444 100644 --- a/src/events/voiceStateUpdate.js +++ b/src/events/voiceStateUpdate.js @@ -61,7 +61,7 @@ export default { const now = Date.now(); if (channelCreationCooldown.has(cooldownKey)) { const lastCreation = channelCreationCooldown.get(cooldownKey); -if (now - lastCreation < VOICE_CREATE_COOLDOWN_MS) { + if (now - lastCreation < VOICE_CREATE_COOLDOWN_MS) { logger.warn(`User ${member.id} is on cooldown for channel creation`); return; } @@ -189,7 +189,6 @@ if (now - lastCreation < VOICE_CREATE_COOLDOWN_MS) { const channelName = sanitizeVoiceChannelName(finalName); -const channelName = sanitizeVoiceChannelName(finalName); if (!member.voice?.channel || member.voice.channel.id !== triggerChannel.id) { logger.debug(`Member ${member.id} no longer in trigger channel ${triggerChannel.id}, aborting temporary channel creation`); channelCreationCooldown.delete(cooldownKey); @@ -198,9 +197,9 @@ const channelName = sanitizeVoiceChannelName(finalName); const tempChannel = await guild.channels.create({ name: channelName, -type: ChannelType.GuildVoice, + type: ChannelType.GuildVoice, parent: triggerChannel.parentId, -userLimit: userLimit === 0 ? undefined : userLimit, + userLimit: userLimit === 0 ? undefined : userLimit, bitrate: bitrate, permissionOverwrites: [ { @@ -325,6 +324,3 @@ function trimCooldownMapIfNeeded() { channelCreationCooldown.delete(entries[index][0]); } } - - - From 2f8ed2bdb8f017009b8c5cd5e901bf1fe72f8ff5 Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 1 Jul 2026 18:26:14 +0700 Subject: [PATCH 80/86] Fix critical bugs in app.js: CORS wildcard handling, rate limiter memory leak, handler type validation, port errors, and request timeouts Fixes: - #1: Proper CORS wildcard handling - #2: Rate limiter memory leak with cleanup interval - #3: Handler type validation for named exports - #4: Comprehensive OS error handling for port binding - #6: Clarified database status checks - #10: Added request timeout configuration" --- src/app.js | 102 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/src/app.js b/src/app.js index c199e62e2a..29993b1cba 100644 --- a/src/app.js +++ b/src/app.js @@ -110,13 +110,18 @@ class TitanBot extends Client { const host = process.env.WEB_HOST || '0.0.0.0'; const corsOrigin = this.config.api?.cors?.origin || '*'; + // FIX #1: Unsafe CORS Configuration - Properly handle wildcard app.use((req, res, next) => { const allowedOrigins = Array.isArray(corsOrigin) ? corsOrigin : [corsOrigin]; const origin = req.headers.origin; + const hasWildcard = allowedOrigins.includes('*'); - if (allowedOrigins.includes('*') || allowedOrigins.includes(origin)) { - res.header('Access-Control-Allow-Origin', origin || '*'); + if (hasWildcard) { + res.header('Access-Control-Allow-Origin', '*'); + } else if (allowedOrigins.includes(origin)) { + res.header('Access-Control-Allow-Origin', origin); } + res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); @@ -126,9 +131,32 @@ class TitanBot extends Client { next(); }); + // FIX #2: Memory Leak in Rate Limiter - Add cleanup logic const requestCounts = new Map(); const windowMs = 60000; const maxRequests = this.config.api?.rateLimit?.max || 100; + const cleanupIntervalMs = 60000; // Cleanup every minute + + // Cleanup stale entries to prevent memory leak + setInterval(() => { + const now = Date.now(); + const windowStart = now - windowMs; + let cleaned = 0; + + for (const [ip, times] of requestCounts) { + const validTimes = times.filter(t => t > windowStart); + if (validTimes.length === 0) { + requestCounts.delete(ip); + cleaned++; + } else if (validTimes.length < times.length) { + requestCounts.set(ip, validTimes); + } + } + + if (cleaned > 0) { + logger.debug(`Rate limiter cleanup: removed ${cleaned} stale IP entries`); + } + }, cleanupIntervalMs); app.use((req, res, next) => { const ip = req.ip; @@ -150,8 +178,17 @@ class TitanBot extends Client { next(); }); + // FIX #6: Database Status Check Ambiguity - Clarify states app.get('/health', (req, res) => { - const dbStatus = this.db?.getStatus?.() || { isDegraded: 'unknown' }; + if (!this.db) { + return res.status(503).json({ + status: 'unhealthy', + reason: 'Database not initialized', + timestamp: new Date().toISOString() + }); + } + + const dbStatus = this.db.getStatus(); const status = { status: 'healthy', timestamp: new Date().toISOString(), @@ -166,19 +203,28 @@ class TitanBot extends Client { }); app.get('/ready', (req, res) => { - const dbStatus = this.db?.getStatus?.() || { isDegraded: true }; + if (!this.db) { + return res.status(503).json({ + ready: false, + reason: 'Database not initialized' + }); + } + + const dbStatus = this.db.getStatus(); const isReady = this.isReady() && !dbStatus.isDegraded; if (isReady) { return res.status(200).json({ ready: true, - message: 'Bot is ready' + message: 'Bot is ready', + database: dbStatus.connectionType }); } res.status(503).json({ ready: false, - reason: !this.isReady() ? 'Bot not Ready' : 'Database degraded' + reason: !this.isReady() ? 'Bot not ready' : 'Database degraded', + database: dbStatus }); }); @@ -190,19 +236,38 @@ class TitanBot extends Client { }); }); + // FIX #10: Missing Request Timeout Configuration + app.use((req, res, next) => { + req.setTimeout(30000); // 30 seconds per request + res.setTimeout(30000); + next(); + }); + const startServer = (port, attempt = 0) => { let hasStartedListening = false; const server = app.listen(port, host, () => { hasStartedListening = true; this.webServer = server; + // Apply additional server timeouts + server.keepAliveTimeout = 65000; + server.requestTimeout = 60000; startupLog(`āœ… Web Server running on ${host}:${port}`); startupLog(`Health endpoint: http://localhost:${port}/health`); startupLog(`Ready endpoint: http://localhost:${port}/ready`); }); + // FIX #4: Unvalidated Port Binding Errors - Handle OS errors properly + const OS_ERRORS = { + 'EADDRINUSE': 'Port is already in use', + 'EACCES': 'Permission denied (port < 1024 requires elevated privileges)', + 'EPERM': 'Operation not permitted', + 'ENOTFOUND': 'Host not found', + 'EHOSTUNREACH': 'Host is unreachable', + }; + server.on('error', (error) => { const errorCode = error?.code || 'UNKNOWN_ERROR'; - const errorMessage = error?.message || 'Unknown server error'; + const errorMessage = OS_ERRORS[errorCode] || error?.message || 'Unknown server error'; if (!hasStartedListening && errorCode === 'EADDRINUSE' && attempt < maxPortRetryAttempts) { const nextPort = port + 1; @@ -211,6 +276,11 @@ class TitanBot extends Client { return; } + if (!hasStartedListening && errorCode === 'EACCES') { + logger.error(`āŒ Cannot bind to port ${port}: ${errorMessage}`); + process.exit(1); + } + if (hasStartedListening && errorCode === 'EADDRINUSE') { logger.warn(`Web server reported a duplicate bind warning on ${host}:${port}, but the bot remains online.`); return; @@ -278,9 +348,21 @@ class TitanBot extends Client { for (const handler of handlers) { try { const module = await import(`./handlers/${handler.path}.js`); - const loaderFn = handler.type.startsWith('named:') - ? module[handler.type.split(':')[1]] - : module.default; + + // FIX #3: Type Error in Handler Loader - Add validation + let loaderFn; + if (handler.type.startsWith('named:')) { + const parts = handler.type.split(':'); + if (parts.length < 2 || !parts[1]) { + throw new Error(`Invalid handler type format: "${handler.type}". Expected "named:exportName"`); + } + loaderFn = module[parts[1]]; + if (!loaderFn) { + throw new Error(`Export "${parts[1]}" not found in ${handler.path}`); + } + } else { + loaderFn = module.default; + } if (typeof loaderFn === 'function') { await loaderFn(this); From fd47493b74c21ce258bac18e2610edf65a4c749f Mon Sep 17 00:00:00 2001 From: arand606 Date: Wed, 1 Jul 2026 18:27:24 +0700 Subject: [PATCH 81/86] Update bot listening name to 'Made By Vecxson' --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 5f028223aa..06d099f813 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -30,7 +30,7 @@ export const botConfig = { type: 0, // 0 = Playing }, { - name: "Syntax Errors", + name: "Made By Vecxson", type: 2, // 2 = Listening }, { From 27e0a4c431d9de26b37b0bd38654cc738fb43fab Mon Sep 17 00:00:00 2001 From: arand606 Date: Thu, 2 Jul 2026 17:58:13 +0700 Subject: [PATCH 82/86] Update bot.js --- src/config/bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/bot.js b/src/config/bot.js index 06d099f813..db96b2118f 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -15,7 +15,7 @@ export const botConfig = { status: "online", // How often to switch statuses in milliseconds (30000 = 30 seconds) - cycleInterval: 11000, + cycleInterval: 10000, // Activity lines shown under the bot name. // `type` number mapping from Discord: From 690c0e445304423352c2fae34313dd3eb93e9d73 Mon Sep 17 00:00:00 2001 From: arand606 Date: Thu, 2 Jul 2026 17:59:03 +0700 Subject: [PATCH 83/86] Update ready.js --- src/events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/ready.js b/src/events/ready.js index 098c7eb6fb..e92548bc4e 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -30,7 +30,7 @@ export default { activities: [activities[currentIndex]], status: status }); - }, cycleInterval || 11000); + }, cycleInterval || 10000); } else { // Fallback if no activities configured client.user.setPresence(config.bot.presence); From f8245df8ff664a9465d41fee2de1f177b6e2435f Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 3 Jul 2026 11:21:11 +0700 Subject: [PATCH 84/86] Add slash command to toggle bot avatar (toggleAvatar.js) --- commands/toggleAvatar.js | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 commands/toggleAvatar.js diff --git a/commands/toggleAvatar.js b/commands/toggleAvatar.js new file mode 100644 index 0000000000..307861078f --- /dev/null +++ b/commands/toggleAvatar.js @@ -0,0 +1,70 @@ +const { SlashCommandBuilder } = require('discord.js'); +const fs = require('fs'); +const path = require('path'); + +// File paths +const STATE_FILE = path.join(__dirname, '..', 'data', 'avatarState.json'); // ensure data/ exists +const ASSETS = { + animated: path.join(__dirname, '..', 'assets', 'avatar_anim.gif'), + static: path.join(__dirname, '..', 'assets', 'avatar_static.png'), +}; + +// helper to read/write state +function readState() { + try { + return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')); + } catch { + return { enabled: false }; + } +} +function writeState(state) { + fs.mkdirSync(path.dirname(STATE_FILE), { recursive: true }); + fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2)); +} + +module.exports = { + data: new SlashCommandBuilder() + .setName('toggleavatar') + .setDescription('Toggle the bot animated avatar on/off (bot owner only)') + .addStringOption(opt => + opt.setName('state') + .setDescription('on / off (omit to toggle)') + .setRequired(false) + .addChoices( + { name: 'on', value: 'on' }, + { name: 'off', value: 'off' } + ) + ), + async execute(interaction) { + // Restrict to bot owner(s) + const ownerId = process.env.OWNER_ID; // set this in env or replace with an ID + if (!ownerId || interaction.user.id !== ownerId) { + return interaction.reply({ content: 'Only the bot owner can run this command.', ephemeral: true }); + } + + await interaction.deferReply({ ephemeral: true }); + + const arg = interaction.options.getString('state'); + const state = readState(); + let newEnabled = state.enabled; + + if (arg === 'on') newEnabled = true; + else if (arg === 'off') newEnabled = false; + else newEnabled = !newEnabled; + + const assetPath = newEnabled ? ASSETS.animated : ASSETS.static; + if (!fs.existsSync(assetPath)) { + return interaction.editReply({ content: `Missing asset: ${assetPath}. Add the file and try again.` }); + } + + try { + const buffer = fs.readFileSync(assetPath); + await interaction.client.user.setAvatar(buffer); + writeState({ enabled: newEnabled }); + return interaction.editReply({ content: `Animated avatar ${newEnabled ? 'enabled' : 'disabled'}.` }); + } catch (err) { + console.error('Failed to set avatar:', err); + return interaction.editReply({ content: `Failed to set avatar: ${err.message}` }); + } + }, +}; From 2f8f48c51fff9abdd6f4be2c0485db83d879af79 Mon Sep 17 00:00:00 2001 From: arand606 Date: Fri, 3 Jul 2026 19:37:45 +0700 Subject: [PATCH 85/86] Fix: Use PermissionFlagsBits enums instead of strings in permission overrides --- src/events/voiceStateUpdate.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/events/voiceStateUpdate.js b/src/events/voiceStateUpdate.js index aef8abe444..0e65167b16 100644 --- a/src/events/voiceStateUpdate.js +++ b/src/events/voiceStateUpdate.js @@ -197,18 +197,18 @@ export default { const tempChannel = await guild.channels.create({ name: channelName, - type: ChannelType.GuildVoice, + type: ChannelType.GuildVoice, parent: triggerChannel.parentId, - userLimit: userLimit === 0 ? undefined : userLimit, + userLimit: userLimit === 0 ? undefined : userLimit, bitrate: bitrate, permissionOverwrites: [ { id: member.id, - allow: ['Connect', 'Speak', 'PrioritySpeaker', 'MoveMembers'] + allow: [PermissionFlagsBits.Connect, PermissionFlagsBits.Speak, PermissionFlagsBits.PrioritySpeaker, PermissionFlagsBits.MoveMembers] }, { id: guild.id, - allow: ['Connect', 'Speak'] + allow: [PermissionFlagsBits.Connect, PermissionFlagsBits.Speak] } ] }); From b2fe68b42deb2f8e996ade4f5f68398f8c7c08b4 Mon Sep 17 00:00:00 2001 From: arand606 Date: Sat, 4 Jul 2026 13:48:12 +0700 Subject: [PATCH 86/86] Delete src/commands/Utility/announce.js --- src/commands/Utility/announce.js | 142 ------------------------------- 1 file changed, 142 deletions(-) delete mode 100644 src/commands/Utility/announce.js diff --git a/src/commands/Utility/announce.js b/src/commands/Utility/announce.js deleted file mode 100644 index f62010dca1..0000000000 --- a/src/commands/Utility/announce.js +++ /dev/null @@ -1,142 +0,0 @@ -import { SlashCommandBuilder, PermissionFlagsBits, ChannelType } from 'discord.js'; -import { createEmbed, errorEmbed, successEmbed } from '../../utils/embeds.js'; -import { InteractionHelper } from '../../utils/interactionHelper.js'; -import { logger } from '../../utils/logger.js'; - -const EPHEMERAL = 1 << 6; - -export default { - data: new SlashCommandBuilder() - .setName('announce') - .setDescription('Make the bot send an announcement message. (Manage Server required)') - .addChannelOption(option => - option - .setName('channel') - .setDescription('Channel to send the announcement in. Defaults to the current channel.') - .addChannelTypes(ChannelType.GuildText, ChannelType.GuildAnnouncement) - .setRequired(false) - ) - .addStringOption(option => - option - .setName('message') - .setDescription('The announcement message to send') - .setRequired(true) - ) - .addRoleOption(option => - option - .setName('role') - .setDescription('Optional role to mention in the announcement') - .setRequired(false) - ) - .addBooleanOption(option => - option - .setName('everyone') - .setDescription('If true, mention @everyone in the announcement') - .setRequired(false) - ) - .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), - - category: 'utility', - - async execute(interaction, config, client) { - const deferSuccess = await InteractionHelper.safeDefer(interaction); - if (!deferSuccess) { - logger.warn(`Announce interaction defer failed`, { - userId: interaction.user.id, - guildId: interaction.guildId, - commandName: 'announce' - }); - return; - } - - try { - // Safer member permission handling across interaction contexts - const memberPerms = interaction.memberPermissions ?? interaction.member?.permissions; - if (!memberPerms?.has(PermissionFlagsBits.ManageGuild)) { - return await InteractionHelper.safeEditReply(interaction, { - embeds: [errorEmbed('Permission Denied', 'You need the **Manage Server** permission to use this command.')], - flags: EPHEMERAL, - }); - } - - const targetChannel = interaction.options.getChannel('channel') || interaction.channel; - const rawMessage = interaction.options.getString('message'); - const messageText = rawMessage ? rawMessage.trim() : ''; - const pingRole = interaction.options.getRole('role'); - const mentionEveryone = interaction.options.getBoolean('everyone'); - - if (!messageText) { - return await InteractionHelper.safeEditReply(interaction, { - embeds: [errorEmbed('Invalid Message', 'Please provide a non-empty announcement message.')], - flags: EPHEMERAL, - }); - } - - if (!targetChannel || !targetChannel.isTextBased()) { - return await InteractionHelper.safeEditReply(interaction, { - embeds: [errorEmbed('Invalid Channel', 'Please provide a valid text channel to send the announcement in.')], - flags: EPHEMERAL, - }); - } - - const botMember = interaction.guild?.members?.me ?? null; - const botPerms = botMember ? targetChannel.permissionsFor(botMember) : targetChannel.permissionsFor(client.user?.id); - if (!botPerms?.has(PermissionFlagsBits.SendMessages)) { - return await InteractionHelper.safeEditReply(interaction, { - embeds: [errorEmbed('Missing Permission', `I don't have permission to send messages in ${targetChannel}.`)], - flags: EPHEMERAL, - }); - } - - // Build the announcement content and allowedMentions safely - let prefix = ''; - const allowedMentions = { parse: [] }; - - if (pingRole) { - prefix = `<@&${pingRole.id}> `; - // allow only that role to be mentioned (prevents accidental pings) - allowedMentions.roles = [pingRole.id]; - } else if (mentionEveryone) { - // Check bot has mention everyone permission - if (!botPerms.has(PermissionFlagsBits.MentionEveryone)) { - return await InteractionHelper.safeEditReply(interaction, { - embeds: [errorEmbed('Missing Permission', "I don't have permission to mention @everyone in that channel.")], - flags: EPHEMERAL, - }); - } - prefix = '@everyone '; - allowedMentions.parse = ['everyone']; - } - - // Send the announcement - const sentMessage = await targetChannel.send({ content: `${prefix}${messageText}`, allowedMentions }); - - // If the channel is a News/Announcement channel, try to crosspost so followers receive it - if (targetChannel.type === ChannelType.GuildAnnouncement && typeof sentMessage.crosspost === 'function') { - try { - await sentMessage.crosspost(); - } catch (e) { - logger.warn('Failed to crosspost announcement:', e); - } - } - - const success = successEmbed('Announcement Sent', `Your announcement was sent in ${targetChannel}.`); - if (typeof success.addFields === 'function') { - success.addFields({ name: 'Link', value: `[Jump to message](${sentMessage.url})` }); - } - - await InteractionHelper.safeEditReply(interaction, { - embeds: [success], - flags: EPHEMERAL, - }); - - logger.info(`Announcement sent by ${interaction.user.tag} in ${interaction.guildId} -> ${targetChannel.id}`); - } catch (error) { - logger.error('Announce command error:', error); - await InteractionHelper.safeEditReply(interaction, { - embeds: [errorEmbed('Failed to Send', 'An unexpected error occurred while sending the announcement.')], - flags: EPHEMERAL, - }); - } - } -};