├── .gitignore
├── LICENSE
├── README.md
├── admin
├── dashboard.js
├── public
│ ├── botProfile.css
│ ├── commands.css
│ ├── config.css
│ ├── errors.css
│ ├── guilds.css
│ └── style.css
└── views
│ ├── botprofile.html
│ ├── commands.html
│ ├── config.html
│ ├── errors.html
│ ├── guilds.html
│ └── index.html
├── cli.js
├── config.json
├── contributing.md
├── setup.js
└── src
├── commands
└── Community
│ └── ping.js
├── events
└── handlers
│ ├── guildJoinLogs.js
│ ├── guildLeaveLogs.js
│ ├── interactionCreate.js
│ ├── prefixCreate.js
│ └── ready.js
├── functions
└── handlers
│ ├── antiCrash.js
│ ├── functionHandler.js
│ ├── handelEvents.js
│ ├── handleCommands.js
│ ├── prefixHandler.js
│ ├── requiredIntents.js
│ ├── similarity.js
│ └── watchFolders.js
├── index.js
└── messages
└── Community
└── ping.js
/.gitignore:
--------------------------------------------------------------------------------
1 | package.json
2 | package-lock.json
3 | node_modules/
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | Copyright 2024 Ethical Programmer
179 |
180 | Licensed under the Apache License, Version 2.0 (the "License");
181 | you may not use this file except in compliance with the License.
182 | You may obtain a copy of the License at
183 |
184 | http://www.apache.org/licenses/LICENSE-2.0
185 |
186 | Unless required by applicable law or agreed to in writing, software
187 | distributed under the License is distributed on an "AS IS" BASIS,
188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189 | See the License for the specific language governing permissions and
190 | limitations under the License.
191 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 |
5 |
6 |    
7 |
8 |
9 |
10 |
11 |
12 | # discoBase
13 |
14 | **discoBase** is a lightweight command and event handler package for Discord bots, enabling easy management of commands and events with a structured file organization and dynamic loading system. It allows you to build almost any advanced Discord bot effortlessly.
15 |
16 | ```
17 | ✨ Supports Latest Discord.js v14.
18 | ```
19 |
20 | ## 🎉 New Update (v2.0.0) 🎉
21 | - 🚀 Enhanced Terminal Look: Experience a cleaner and more modern terminal display with colorful logs for success, errors, and info messages.
22 | - 📝 Auto-Generated Files: Commands, events, and prefix commands now come with pre-built templates to save you time.
23 | - ⚡ Optimized File Watching: Automatic detection and template insertion for new command, event, and prefix files.
24 | - 🔍 **Error Logging:** Errors encountered during runtime are automatically logged into an `errors` folder for easier debugging.
25 | - 📊 **Discobase Dashboard:** A comprehensive dashboard running on localhost allows you to:
26 | - View statistics such as bot guilds, command counts, and user counts.
27 | - Monitor recent activities (e.g., command creation, deletion, and changes).
28 | - Manage bot settings like banner, avatar, and name directly from the dashboard.
29 |
30 |
31 | ## Features
32 |
33 | - 🎉 Command Handler
34 | - 📅 Event Handler
35 | - ⚙️ Advanced Customization
36 | - 🚀 Asynchronous Support
37 | - 🔄 Dynamic Reloading
38 | - 🛠️ Modular Structure
39 | - 🛡 Never Crash
40 | - 🌐 Compatibility with Advanced Discord Bots
41 | - 🔤 Prefix Commands Support
42 | - ➗ Slash Commands Support
43 | - 🔔 Automatic Detection of Missing Intents
44 | - ⚙️ **Configurable Function Execution:** Allows for setting properties such as `once`, `interval`, `retryAttempts`, `maxExecution`, and `initializer` in your functions to control execution patterns. Ideal for scheduling tasks or retrying operations with ease.
45 | - 🗂️ **Error Logging:** Automatic logging of runtime errors into an `errors` folder.
46 | - 📊 **Discobase Dashboard:** View and manage your bot's statistics and settings easily.
47 | - 🔧 **Discobase Generate Command:** Generate new commands and events with ease. For example:
48 | run this in your terminal after setuping discobase!
49 | ```bash
50 | npm run generate
51 | ```
52 |
53 | ## Installation
54 |
55 | To create a new **discoBase** project, run the following commands:
56 |
57 |
58 | ```bash
59 | npx create-discobase@latest my-project
60 | ```
61 |
62 | You can also create a new project in the current directory without specifying a project name:
63 |
64 | ```bash
65 | npx create-discobase@latest
66 | ```
67 | This will generate a new **discoBase** project in the current directory.
68 |
69 | ## Useful Addon
70 | - [Discobase](https://www.npmjs.com/package/discobase)
71 |
72 |
73 | ## Configuration
74 |
75 | To run this project, you will need to provide the necessary values in the config.json file located in the root directory. The structure of the file is as follows:
76 |
77 |
78 | | Parameter | Type | Description |
79 | | :------------------------------| :------- | :----------------------------------------------------------- |
80 | | `bot.token` | `string` | **Required**. Your Discord bot token |
81 | | `bot.id` | `string` | **Required**. The ID of your Discord bot |
82 | | `bot.admins` | `array` | **Optional**. List of admin user IDs |
83 | | `bot.ownerId` | `string` | **Optional**. The owner's user ID |
84 | | `bot.developerCommandsServerIds`| `array` | **Optional**. Server IDs where developer commands are enabled |
85 | | `database.mongodbUrl` | `string` | **Optional**. MongoDB connection URL |
86 | | `logging.guildJoinLogsId` | `string` | **Optional**. Channel ID for guild join logs |
87 | | `logging.guildLeaveLogsId` | `string` | **Optional**. Channel ID for guild leave logs |
88 | | `logging.commandLogsChannelId` | `string` | **Optional**. Channel ID for command logs |
89 | | `logging.errorLogs` | `string` | **Optional**. Webhook URL for error logging |
90 | | `prefix.value` | `string` | **Optional**. Command prefix for non-slash commands |
91 |
92 |
93 |
94 | ## Command Options
95 |
96 | | Option | Type | Description |
97 | | :------------------ | :---------- | :--------------------------------------------------------------------------------------------------- |
98 | | `ownerOnly` | `boolean` | **Optional**. If `true`, the command can only be run by the bot owner. |
99 | | `adminOnly` | `boolean` | **Optional**. If `true`, the command can only be used by bot admins specified in the config file. |
100 | | `devOnly` | `boolean` | **Optional**. If `true`, the command is only registered/run in specific developer servers. |
101 | | `botPermissions` | `array` | **Optional**. List of permissions the bot needs to execute the command (e.g., `'SendMessages'`, `'ManageChannels'`). |
102 | | `userPermissions` | `array` | **Optional**. List of permissions the user needs to execute the command (e.g., `'Administrator'`, `'KickMembers'`). |
103 | | `cooldown` | `number` | **Optional**. The cooldown time in seconds before the command can be reused. Default is 3 seconds. |
104 |
105 |
106 | ## Function Options
107 | | Property | Type | Description |
108 | |------------------|------------|------------------------------------------------------------------------------------------------------|
109 | | `once` | `boolean` | If `true`, the function will only execute once. If `false`, it can be executed repeatedly. |
110 | | `interval` | `number` | The time interval (in milliseconds) between repeated executions of the function. |
111 | | `retryAttempts` | `number` | Specifies the number of retry attempts if the function fails during execution. |
112 | | `maxExecution` | `number` | Defines the maximum number of times the function can execute. |
113 | | `initializer` | `number` | Initial value or state to use when starting the function; can be used for setup or as a counter. |
114 |
115 | ```js
116 | const exampleFunction = async () => {
117 | console.log("Function executed successfully.");
118 | };
119 |
120 | exampleFunction.config = {
121 | once: true,
122 | interval: 10000,
123 | retryAttempts: 3,
124 | maxExecution: 5,
125 | initializer: 10
126 | };
127 |
128 | module.exports = exampleFunction;
129 |
130 | ```
131 |
132 |
133 | ## Contributing
134 |
135 | Contributions are always welcome!
136 |
137 | See `contributing.md` for ways to get started.
138 |
139 | Please adhere to this project's `code of conduct`.
140 |
141 |
142 | ## Show your support
143 |
144 | Give a ⭐️ if this project helped you!
145 |
146 |
147 |
148 |
149 |
150 | ## Feedback & Suggestion
151 |
152 | If you have any feedback or suggestion, please reach out to us at [Discord Community](https://discord.gg/ethical-programmer-s-1188398653530984539)
153 |
154 |
155 | ## Support
156 |
157 | For support & questions, join our Discord server: [Discord Community](https://discord.gg/ethical-programmer-s-1188398653530984539).
--------------------------------------------------------------------------------
/admin/dashboard.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const fs = require('fs');
4 | const path = require('path');
5 | const app = express();
6 | const port = 3000;
7 | const client = require('../src/index')
8 | const multer = require('multer');
9 | const upload = multer();
10 | const { getActivities } = require('../src/functions/handlers/handleCommands');
11 |
12 | app.use(bodyParser.urlencoded({ extended: true }));
13 | app.use(express.static(path.join(__dirname, 'public')));
14 | app.use(upload.fields([{ name: 'new-bot-avatar' }, { name: 'new-bot-banner' }]));
15 |
16 |
17 |
18 | function loadConfig() {
19 | const configPath = path.join(__dirname, '../config.json');
20 | if (fs.existsSync(configPath)) {
21 | return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
22 | }
23 | return {};
24 | }
25 |
26 | app.get('/', (req, res) => {
27 | res.sendFile(path.join(__dirname, 'views', 'index.html'));
28 | });
29 |
30 | app.get('/bot', (req, res) => {
31 | res.sendFile(path.join(__dirname, 'views', 'botprofile.html'));
32 | });
33 |
34 | app.get('/config', (req, res) => {
35 | res.sendFile(path.join(__dirname, 'views', 'config.html'));
36 | });
37 |
38 | app.get('/commands', (req, res) => {
39 | res.sendFile(path.join(__dirname, 'views', 'commands.html'));
40 | });
41 |
42 | app.get('/errors', (req, res) => {
43 | res.sendFile(path.join(__dirname, 'views', 'errors.html'));
44 | });
45 |
46 | app.get('/guilds', (req, res) => {
47 | res.sendFile(path.join(__dirname, 'views', 'guilds.html'));
48 | });
49 |
50 | app.get('/api/bot-info', async (req, res) => {
51 | try {
52 |
53 | const fetchedUser = await client.users.fetch(client.user.id, { force: true });
54 | const botStatus = client.presence.status;
55 | const botName = client.user.username;
56 | const botAvatar = `https://cdn.discordapp.com/avatars/${client.user.id}/${client.user.avatar}.png`;
57 |
58 | const botId = client.user.id;
59 | const botBanner = `https://cdn.discordapp.com/banners/${client.user.id}/${fetchedUser.banner}.png`;
60 |
61 | const isVerified = client.user.verified;
62 | res.json({
63 | botStatus,
64 | botName,
65 | botId,
66 | botBanner,
67 | botAvatar,
68 | isVerified
69 | })
70 | } catch (err) {
71 | console.error('Error fetching bot info:', err);
72 | res.status(500).json({ error: 'Internal Server Error' });
73 | }
74 | })
75 |
76 | app.post('/api/update-bot', async (req, res) => {
77 | const { newBotName } = req.body;
78 |
79 | if (newBotName) {
80 | try {
81 | await client.user.setUsername(newBotName);
82 | } catch (error) {
83 | console.error('Error updating bot name:', error);
84 | return res.json({ success: false, message: 'Failed to update bot name' });
85 | }
86 | }
87 |
88 | // Update bot avatar if provided
89 | const avatarFile = req.files['new-bot-avatar']?.[0];
90 | if (avatarFile) {
91 | try {
92 | await client.user.setAvatar(avatarFile.buffer); // Use buffer for the image
93 | } catch (error) {
94 | console.error('Error updating bot avatar:', error);
95 | return res.json({ success: false, message: 'Failed to update bot avatar' });
96 | }
97 | }
98 |
99 | const bannerFile = req.files['new-bot-banner']?.[0];
100 | if (bannerFile) {
101 | try {
102 | await client.user.setBanner(bannerFile.buffer);
103 | } catch (error) {
104 | console.error('Error updating bot banner:', error);
105 | return res.json({ success: false, message: 'Failed to update bot banner' });
106 | }
107 | }
108 |
109 | return res.json({ success: true });
110 | });
111 |
112 | app.get('/api/guilds', async (req, res) => {
113 | const guildsData = client.guilds.cache.map(guild => ({
114 | id: guild.id,
115 | name: guild.name,
116 | icon: guild.iconURL(),
117 | memberCount: guild.memberCount,
118 | }));
119 |
120 | res.json(guildsData);
121 | });
122 |
123 | app.get('/api/bot-stats', async (req, res) => {
124 | try {
125 | const totalServers = client.guilds.cache.size;
126 | const totalCommands = (client.commands ? client.commands.size : 0) + (client.prefix ? client.prefix.size : 0);
127 | const botName = client.user.username;
128 | const botIcon = `https://cdn.discordapp.com/avatars/${client.user.id}/${client.user.avatar}.png`;
129 | let totalUsers = 0;
130 | client.guilds.cache.forEach(guild => {
131 | totalUsers += guild.memberCount;
132 | });
133 |
134 | res.json({
135 | totalServers,
136 | totalUsers,
137 | totalCommands,
138 | botName,
139 | botIcon
140 | });
141 | } catch (err) {
142 | console.error('Error fetching bot stats:', err);
143 | res.status(500).json({ error: 'Internal Server Error' });
144 | }
145 | });
146 |
147 | app.post('/update-config', (req, res) => {
148 | // Load the existing config
149 | const currentConfig = loadConfig();
150 |
151 | // Update only the fields that are provided
152 | const newConfig = {
153 | bot: {
154 | ...currentConfig.bot, // Keep existing values
155 | ...(req.body.token && { token: req.body.token }), // Update token if provided
156 | ...(req.body.id && { id: req.body.id }), // Update id if provided
157 | ...(req.body.admins && { admins: req.body.admins.split(',') }), // Update admins if provided
158 | ...(req.body.ownerId && { ownerId: req.body.ownerId }), // Update ownerId if provided
159 | ...(req.body.developerCommandsServerIds && {
160 | developerCommandsServerIds: req.body.developerCommandsServerIds.split(','),
161 | }), // Update developerCommandsServerIds if provided
162 | },
163 | database: {
164 | ...(currentConfig.database || {}),
165 | ...(req.body.mongodbUrl && { mongodbUrl: req.body.mongodbUrl }),
166 | },
167 | logging: {
168 | ...(currentConfig.logging || {}),
169 | ...(req.body.guildJoinLogsId && { guildJoinLogsId: req.body.guildJoinLogsId }),
170 | ...(req.body.guildLeaveLogsId && { guildLeaveLogsId: req.body.guildLeaveLogsId }),
171 | ...(req.body.commandLogsChannelId && { commandLogsChannelId: req.body.commandLogsChannelId }),
172 | ...(req.body.errorLogs && { errorLogs: req.body.errorLogs }),
173 | },
174 | prefix: {
175 | ...(currentConfig.prefix || {}),
176 | ...(req.body.prefix && { value: req.body.prefix }),
177 | },
178 | };
179 |
180 | fs.writeFileSync(path.join(__dirname, '../config.json'), JSON.stringify(newConfig, null, 2));
181 | res.redirect('/config?success=true');
182 | });
183 |
184 | app.get('/api/commands', (req, res) => {
185 | const slashCommandsDir = path.join(__dirname, '../src/commands');
186 | const prefixCommandsDir = path.join(__dirname, '../src/messages');
187 | const commands = {
188 | slash: [],
189 | prefix: []
190 | };
191 |
192 | // A function to read commands from a directory
193 | function readCommands(dir, commandArray, type) {
194 | return new Promise((resolve, reject) => {
195 | fs.readdir(dir, (err, files) => {
196 | if (err) {
197 | return reject(`Error reading ${type} commands`);
198 | }
199 |
200 | const promises = files.map(file => {
201 | const filePath = path.join(dir, file);
202 | return new Promise((resolveFile, rejectFile) => {
203 | fs.stat(filePath, (err, stats) => {
204 | if (err) {
205 | return rejectFile('Error reading file stats');
206 | }
207 |
208 | if (stats.isDirectory()) {
209 | readCommands(filePath, commandArray, type).then(resolveFile).catch(rejectFile);
210 | } else if (path.extname(file) === '.js') {
211 | const command = require(filePath);
212 | if (type === 'slash') {
213 | commandArray.push({
214 | name: command.data.name,
215 | description: command.data.description
216 | });
217 | } else {
218 | commandArray.push({
219 | name: command.name,
220 | description: command.description
221 | });
222 | }
223 | resolveFile();
224 | } else {
225 | resolveFile(); // Handle non-JS files gracefully
226 | }
227 | });
228 | });
229 | });
230 |
231 | // Wait for all promises to resolve
232 | Promise.all(promises).then(resolve).catch(reject);
233 | });
234 | });
235 | }
236 |
237 | // Read slash commands and then prefix commands
238 | readCommands(slashCommandsDir, commands.slash, 'slash')
239 | .then(() => readCommands(prefixCommandsDir, commands.prefix, 'prefix'))
240 | .then(() => {
241 | res.json(commands); // Send response after both commands are read
242 | })
243 | .catch(err => {
244 | console.error(err);
245 | res.status(500).json({ error: err });
246 | });
247 | });
248 |
249 | app.get('/api/activities', (req, res) => {
250 | const activities = getActivities();
251 | res.json(activities);
252 | });
253 |
254 | app.get('/api/errors', (req, res) => {
255 | const errorsDir = path.join(__dirname, '../errors'); // Path to the errors folder
256 |
257 | // Ensure that the folder exists
258 | if (!fs.existsSync(errorsDir)) {
259 | return res.json({ errors: [], message: 'No errors found' });
260 | }
261 |
262 | // Read the files from the errors folder
263 | fs.readdir(errorsDir, (err, files) => {
264 | if (err) {
265 | return res.status(500).json({ error: 'Unable to read error logs' });
266 | }
267 |
268 | // Sort files by date (latest first)
269 | files.sort((a, b) => fs.statSync(path.join(errorsDir, b)).mtime - fs.statSync(path.join(errorsDir, a)).mtime);
270 |
271 | // Prepare an array to store errors
272 | const errorLogs = [];
273 |
274 | // Read the content of each error file
275 | files.forEach(file => {
276 | const filePath = path.join(errorsDir, file);
277 | const content = fs.readFileSync(filePath, 'utf8');
278 | errorLogs.push({ fileName: file, content });
279 | });
280 |
281 | // Send the error logs to the front-end
282 | res.json({ errors: errorLogs });
283 | });
284 | });
285 |
286 | // Start the server
287 | app.listen(port, () => {
288 | console.log(`Admin dashboard running at http://localhost:${port}`);
289 | });
290 |
--------------------------------------------------------------------------------
/admin/public/botProfile.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary-color: #a69cec;
3 | --secondary-color: #00cec9;
4 | --background-color: #2d3436;
5 | --card-background: rgba(255, 255, 255, 0.1);
6 | --text-color: #ffffff;
7 | --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
8 | --success-color: #2ecc71;
9 | --error-color: #e74c3c;
10 | }
11 |
12 | * {
13 | margin: 0;
14 | padding: 0;
15 | box-sizing: border-box;
16 | }
17 |
18 | body {
19 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
20 | background: linear-gradient(135deg, var(--background-color), #4834d4);
21 | color: var(--text-color);
22 | line-height: 1.6;
23 | min-height: 100vh;
24 | }
25 |
26 | .navbar {
27 | background-color: rgba(255, 255, 255, 0.1);
28 | backdrop-filter: blur(10px);
29 | padding: 1rem 2rem;
30 | display: flex;
31 | justify-content: space-between;
32 | align-items: center;
33 | position: fixed;
34 | width: 100%;
35 | top: 0;
36 | z-index: 1000;
37 | }
38 |
39 | .logo {
40 | font-size: 1.5rem;
41 | font-weight: bold;
42 | color: var(--text-color);
43 | }
44 |
45 | nav ul {
46 | list-style-type: none;
47 | display: flex;
48 | gap: 2rem;
49 | }
50 |
51 | .nav-link {
52 | color: var(--text-color);
53 | text-decoration: none;
54 | font-weight: bold;
55 | transition: color 0.3s ease, border-bottom 0.3s ease;
56 | padding-bottom: 0.25rem;
57 | }
58 |
59 | .nav-link:hover {
60 | color: var(--secondary-color);
61 | border-bottom: 2px solid var(--secondary-color);
62 | }
63 |
64 | .container {
65 | max-width: 1200px;
66 | margin: 0 auto;
67 | padding: 6rem 2rem 2rem;
68 | }
69 |
70 | .title {
71 | text-align: center;
72 | margin-bottom: 2rem;
73 | font-size: 2.5rem;
74 | color: var(--secondary-color);
75 | }
76 |
77 | .bot-info {
78 | background-color: var(--card-background);
79 | border-radius: 10px;
80 | padding: 2rem;
81 | box-shadow: var(--card-shadow);
82 | margin-bottom: 2rem;
83 | animation: fadeInUp 0.5s ease;
84 | }
85 |
86 | .bot-header {
87 | display: flex;
88 | align-items: center;
89 | margin-bottom: 1rem;
90 | }
91 |
92 | .bot-avatar {
93 | width: 100px;
94 | height: 100px;
95 | border-radius: 50%;
96 | object-fit: cover;
97 | margin-right: 1rem;
98 | border: 3px solid var(--primary-color);
99 | }
100 |
101 | .bot-name-status {
102 | flex-grow: 1;
103 | }
104 |
105 | .bot-name {
106 | font-size: 1.5rem;
107 | color: var(--secondary-color);
108 | margin-bottom: 0.5rem;
109 | }
110 |
111 | .bot-banner {
112 | width: 100%;
113 | height: 200px;
114 | object-fit: cover;
115 | border-radius: 10px;
116 | margin-bottom: 1rem;
117 | }
118 |
119 | .bot-details {
120 | display: grid;
121 | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
122 | gap: 1rem;
123 | }
124 |
125 | .bot-detail {
126 | background-color: rgba(255, 255, 255, 0.05);
127 | padding: 1rem;
128 | border-radius: 5px;
129 | }
130 |
131 | .bot-detail h3 {
132 | color: var(--secondary-color);
133 | margin-bottom: 0.5rem;
134 | }
135 |
136 | .bot-status {
137 | display: inline-block;
138 | padding: 0.25rem 0.5rem;
139 | border-radius: 20px;
140 | font-size: 0.9rem;
141 | font-weight: bold;
142 | }
143 |
144 | .status-online {
145 | background-color: var(--success-color);
146 | }
147 |
148 | .status-offline {
149 | background-color: var(--error-color);
150 | }
151 |
152 | .bot-actions {
153 | margin-top: 2rem;
154 | }
155 |
156 | .bot-actions h3 {
157 | color: var(--secondary-color);
158 | margin-bottom: 1rem;
159 | }
160 |
161 | .form-group {
162 | margin-bottom: 1rem;
163 | }
164 |
165 | .bot-button {
166 | display: flex;
167 | align-items: center;
168 | margin-left: 2rem;
169 | /* Adjust spacing as needed */
170 | }
171 |
172 | .bot-button .nav-link {
173 | display: flex;
174 | align-items: center;
175 | background-color: var(--secondary-color);
176 | padding: 0.5rem 1rem;
177 | border-radius: 20px;
178 | color: var(--text-color);
179 | transition: background-color 0.3s ease;
180 | }
181 |
182 | .bot-button .nav-link:hover {
183 | background-color: #009999;
184 | /* Darker shade for hover effect */
185 | }
186 |
187 | .bot-icon {
188 | width: 40px;
189 | height: 40px;
190 | border-radius: 50%;
191 | overflow: hidden;
192 | margin-right: 0.5rem;
193 | }
194 |
195 | .bot-icon img {
196 | width: 100%;
197 | height: 100%;
198 | object-fit: cover;
199 | }
200 |
201 | .bot-name {
202 | font-weight: bold;
203 | }
204 |
205 |
206 | label {
207 | display: block;
208 | margin-bottom: 0.5rem;
209 | color: var(--secondary-color);
210 | }
211 |
212 | input[type="text"],
213 | input[type="file"] {
214 | width: 100%;
215 | padding: 0.5rem;
216 | border: none;
217 | background-color: rgba(255, 255, 255, 0.1);
218 | color: var(--text-color);
219 | border-radius: 5px;
220 | }
221 |
222 | button {
223 | background-color: var(--primary-color);
224 | color: var(--text-color);
225 | border: none;
226 | padding: 0.5rem 1rem;
227 | border-radius: 5px;
228 | cursor: pointer;
229 | transition: background-color 0.3s ease;
230 | }
231 |
232 | button:hover {
233 | background-color: var(--secondary-color);
234 | }
235 |
236 | @keyframes fadeInUp {
237 | from {
238 | opacity: 0;
239 | transform: translateY(20px);
240 | }
241 |
242 | to {
243 | opacity: 1;
244 | transform: translateY(0);
245 | }
246 | }
247 |
248 | @media (max-width: 768px) {
249 | .container {
250 | padding: 5rem 1rem 1rem;
251 | }
252 |
253 | .bot-details {
254 | grid-template-columns: 1fr;
255 | }
256 |
257 | .bot-header {
258 | flex-direction: column;
259 | text-align: center;
260 | }
261 |
262 | .bot-avatar {
263 | margin-right: 0;
264 | margin-bottom: 1rem;
265 | }
266 | }
--------------------------------------------------------------------------------
/admin/public/commands.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary-color: #a69cec;
3 | --secondary-color: #00cec9;
4 | --background-color: #2d3436;
5 | --card-background: rgba(255, 255, 255, 0.1);
6 | --text-color: #ffffff;
7 | --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
8 | }
9 |
10 | * {
11 | margin: 0;
12 | padding: 0;
13 | box-sizing: border-box;
14 | }
15 |
16 | body {
17 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
18 | background: linear-gradient(135deg, var(--background-color), #4834d4);
19 | color: var(--text-color);
20 | line-height: 1.6;
21 | min-height: 100vh;
22 | }
23 |
24 | .navbar {
25 | background-color: rgba(255, 255, 255, 0.1);
26 | backdrop-filter: blur(10px);
27 | padding: 1rem 2rem;
28 | display: flex;
29 | justify-content: space-between;
30 | align-items: center;
31 | position: fixed;
32 | width: 100%;
33 | top: 0;
34 | z-index: 1000;
35 | }
36 |
37 | .logo {
38 | font-size: 1.5rem;
39 | font-weight: bold;
40 | color: var(--text-color);
41 | }
42 |
43 | nav ul {
44 | list-style-type: none;
45 | display: flex;
46 | gap: 2rem;
47 | }
48 |
49 | .nav-link {
50 | color: var(--text-color);
51 | text-decoration: none;
52 | font-weight: bold;
53 | transition: color 0.3s ease, border-bottom 0.3s ease;
54 | padding-bottom: 0.25rem;
55 | }
56 |
57 | .nav-link:hover {
58 | color: var(--secondary-color);
59 | border-bottom: 2px solid var(--secondary-color);
60 | }
61 |
62 | .container {
63 | max-width: 1200px;
64 | margin: 0 auto;
65 | padding: 6rem 2rem 2rem;
66 | }
67 |
68 | .title {
69 | text-align: center;
70 | margin-bottom: 2rem;
71 | font-size: 2.5rem;
72 | color: var(--secondary-color);
73 | }
74 |
75 | .commands-section {
76 | margin-bottom: 3rem;
77 | }
78 |
79 | h2 {
80 | color: var(--secondary-color);
81 | margin-bottom: 1.5rem;
82 | font-size: 1.8rem;
83 | }
84 |
85 | .command-cards {
86 | display: grid;
87 | grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
88 | gap: 1.5rem;
89 | }
90 |
91 | .command-card {
92 | background-color: var(--card-background);
93 | border-radius: 10px;
94 | padding: 1.5rem;
95 | box-shadow: var(--card-shadow);
96 | transition: transform 0.3s ease, box-shadow 0.3s ease;
97 | opacity: 0;
98 | transform: translateY(20px);
99 | animation: fadeInUp 0.5s ease forwards;
100 | }
101 |
102 | .command-card:hover {
103 | transform: translateY(-5px);
104 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
105 | }
106 |
107 | .bot-button {
108 | display: flex;
109 | align-items: center;
110 | margin-left: 2rem;
111 | /* Adjust spacing as needed */
112 | }
113 |
114 | .bot-button .nav-link {
115 | display: flex;
116 | align-items: center;
117 | background-color: var(--secondary-color);
118 | padding: 0.5rem 1rem;
119 | border-radius: 20px;
120 | color: var(--text-color);
121 | transition: background-color 0.3s ease;
122 | }
123 |
124 | .bot-button .nav-link:hover {
125 | background-color: #009999;
126 | /* Darker shade for hover effect */
127 | }
128 |
129 | .bot-icon {
130 | width: 40px;
131 | height: 40px;
132 | border-radius: 50%;
133 | overflow: hidden;
134 | margin-right: 0.5rem;
135 | }
136 |
137 | .bot-icon img {
138 | width: 100%;
139 | height: 100%;
140 | object-fit: cover;
141 | }
142 |
143 | .bot-name {
144 | font-weight: bold;
145 | }
146 |
147 |
148 | .command-card h3 {
149 | color: var(--primary-color);
150 | margin-bottom: 0.5rem;
151 | font-size: 1.2rem;
152 | }
153 |
154 | .command-card p {
155 | font-size: 0.9rem;
156 | color: rgba(255, 255, 255, 0.8);
157 | }
158 |
159 | @keyframes fadeInUp {
160 | to {
161 | opacity: 1;
162 | transform: translateY(0);
163 | }
164 | }
165 |
166 | @media (max-width: 768px) {
167 | .container {
168 | padding: 5rem 1rem 1rem;
169 | }
170 |
171 | .command-cards {
172 | grid-template-columns: 1fr;
173 | }
174 | }
--------------------------------------------------------------------------------
/admin/public/config.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary-color: #ff6b6b;
3 | --secondary-color: #4ecdc4;
4 | --background-color: #1a1a2e;
5 | --text-color: #ffffff;
6 | --input-background: #2a2a3e;
7 | --input-text: #ffffff;
8 | --button-hover: #ff8787;
9 | }
10 |
11 |
12 | body {
13 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
14 | background: linear-gradient(135deg, #1a1a2e, #16213e, #0f3460);
15 | color: var(--text-color);
16 | display: flex;
17 | justify-content: center;
18 | align-items: center;
19 | padding: 2rem;
20 | }
21 |
22 | .container {
23 | background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
24 | backdrop-filter: blur(10px);
25 | border-radius: 20px;
26 | padding: 2rem;
27 | width: 100%;
28 | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
29 | }
30 |
31 | h1 {
32 | text-align: center;
33 | margin-bottom: 2rem;
34 | color: var(--secondary-color);
35 | font-size: 2.5rem;
36 | text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
37 | }
38 |
39 | form {
40 | display: grid;
41 | gap: 2rem;
42 | }
43 |
44 | .form-row {
45 | display: grid;
46 | grid-template-columns: repeat(3, 1fr);
47 | gap: 1.5rem;
48 | }
49 |
50 | .form-group {
51 | display: flex;
52 | flex-direction: column;
53 | }
54 |
55 | label {
56 | margin-bottom: 0.5rem;
57 | font-weight: bold;
58 | color: var(--secondary-color);
59 | }
60 |
61 | input {
62 | padding: 0.75rem;
63 | border: none;
64 | border-radius: 8px;
65 | background-color: var(--input-background);
66 | color: var(--input-text);
67 | font-size: 1rem;
68 | transition: all 0.3s ease;
69 | }
70 |
71 | input:focus {
72 | outline: none;
73 | box-shadow: 0 0 0 2px var(--secondary-color);
74 | }
75 |
76 | .submit-button {
77 | background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
78 | color: white;
79 | border: none;
80 | padding: 1rem;
81 | border-radius: 8px;
82 | font-size: 1.1rem;
83 | cursor: pointer;
84 | transition: all 0.3s ease;
85 | text-transform: uppercase;
86 | letter-spacing: 1px;
87 | font-weight: bold;
88 | width: 100%;
89 | max-width: 300px;
90 | justify-self: center;
91 | }
92 |
93 | .submit-button:hover {
94 | background: linear-gradient(135deg, var(--button-hover), var(--secondary-color));
95 | transform: translateY(-2px);
96 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
97 | }
98 |
99 | .form-group-special {
100 | position: relative;
101 | overflow: hidden;
102 | }
103 |
104 | .form-group-special::before {
105 | content: '';
106 | position: absolute;
107 | top: -50%;
108 | left: -50%;
109 | width: 200%;
110 | height: 200%;
111 | background: conic-gradient(from 0deg, transparent, var(--primary-color), transparent 30%);
112 | animation: rotate 4s linear infinite;
113 | z-index: -1;
114 | }
115 |
116 | .form-group-special label,
117 | .form-group-special input {
118 | position: relative;
119 | z-index: 1;
120 | }
121 |
122 | @keyframes rotate {
123 | 100% {
124 | transform: rotate(360deg);
125 | }
126 | }
127 |
128 | @media (max-width: 768px) {
129 | .container {
130 | padding: 1.5rem;
131 | }
132 |
133 | h1 {
134 | font-size: 2rem;
135 | }
136 |
137 | .form-row {
138 | grid-template-columns: 1fr;
139 | }
140 | }
--------------------------------------------------------------------------------
/admin/public/errors.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary-color: #6c5ce7;
3 | --secondary-color: #00cec9;
4 | --background-color: #2d3436;
5 | --card-background: rgba(255, 255, 255, 0.1);
6 | --text-color: #ffffff;
7 | --error-color: #ff6b6b;
8 | --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
9 | }
10 |
11 | * {
12 | margin: 0;
13 | padding: 0;
14 | box-sizing: border-box;
15 | }
16 |
17 | body {
18 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
19 | background: linear-gradient(135deg, var(--background-color), #4834d4);
20 | color: var(--text-color);
21 | line-height: 1.6;
22 | min-height: 100vh;
23 | }
24 |
25 | .navbar {
26 | background-color: rgba(255, 255, 255, 0.1);
27 | backdrop-filter: blur(10px);
28 | padding: 1rem 2rem;
29 | display: flex;
30 | justify-content: space-between;
31 | align-items: center;
32 | position: fixed;
33 | width: 100%;
34 | top: 0;
35 | z-index: 1000;
36 | }
37 |
38 | .logo {
39 | font-size: 1.5rem;
40 | font-weight: bold;
41 | color: var(--text-color);
42 | }
43 |
44 | nav ul {
45 | list-style-type: none;
46 | display: flex;
47 | gap: 2rem;
48 | }
49 |
50 | .nav-link {
51 | color: var(--text-color);
52 | text-decoration: none;
53 | font-weight: bold;
54 | transition: color 0.3s ease, border-bottom 0.3s ease;
55 | padding-bottom: 0.25rem;
56 | }
57 |
58 | .nav-link:hover {
59 | color: var(--secondary-color);
60 | border-bottom: 2px solid var(--secondary-color);
61 | }
62 |
63 | .container {
64 | max-width: 1200px;
65 | margin: 0 auto;
66 | padding: 6rem 2rem 2rem;
67 | }
68 |
69 | .title {
70 | text-align: center;
71 | margin-bottom: 2rem;
72 | font-size: 2.5rem;
73 | color: var(--secondary-color);
74 | }
75 |
76 | .error-container {
77 | background-color: var(--card-background);
78 | border-radius: 10px;
79 | padding: 1.5rem;
80 | box-shadow: var(--card-shadow);
81 | margin-bottom: 1.5rem;
82 | transition: transform 0.3s ease, box-shadow 0.3s ease;
83 | opacity: 0;
84 | transform: translateY(20px);
85 | animation: fadeInUp 0.5s ease forwards;
86 | }
87 |
88 | .error-container:hover {
89 | transform: translateY(-5px);
90 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
91 | }
92 |
93 | .error-container h3 {
94 | color: var(--error-color);
95 | margin-bottom: 1rem;
96 | font-size: 1.2rem;
97 | }
98 |
99 | .bot-button {
100 | display: flex;
101 | align-items: center;
102 | margin-left: 2rem;
103 | /* Adjust spacing as needed */
104 | }
105 |
106 | .bot-button .nav-link {
107 | display: flex;
108 | align-items: center;
109 | background-color: var(--secondary-color);
110 | padding: 0.5rem 1rem;
111 | border-radius: 20px;
112 | color: var(--text-color);
113 | transition: background-color 0.3s ease;
114 | }
115 |
116 | .bot-button .nav-link:hover {
117 | background-color: #009999;
118 | /* Darker shade for hover effect */
119 | }
120 |
121 | .bot-icon {
122 | width: 40px;
123 | height: 40px;
124 | border-radius: 50%;
125 | overflow: hidden;
126 | margin-right: 0.5rem;
127 | }
128 |
129 | .bot-icon img {
130 | width: 100%;
131 | height: 100%;
132 | object-fit: cover;
133 | }
134 |
135 | .bot-name {
136 | font-weight: bold;
137 | }
138 |
139 | pre {
140 | background-color: rgba(0, 0, 0, 0.3);
141 | color: #f8f8f2;
142 | padding: 1rem;
143 | border-radius: 5px;
144 | overflow-x: auto;
145 | font-family: 'Courier New', Courier, monospace;
146 | font-size: 0.9rem;
147 | }
148 |
149 | .no-errors {
150 | text-align: center;
151 | font-size: 1.2rem;
152 | color: var(--secondary-color);
153 | }
154 |
155 | @keyframes fadeInUp {
156 | to {
157 | opacity: 1;
158 | transform: translateY(0);
159 | }
160 | }
161 |
162 | @media (max-width: 768px) {
163 | .container {
164 | padding: 5rem 1rem 1rem;
165 | }
166 | }
--------------------------------------------------------------------------------
/admin/public/guilds.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary-color: #a69cec;
3 | --secondary-color: #00cec9;
4 | --background-color: #2d3436;
5 | --card-background: rgba(255, 255, 255, 0.1);
6 | --text-color: #ffffff;
7 | --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
8 | }
9 |
10 | * {
11 | margin: 0;
12 | padding: 0;
13 | box-sizing: border-box;
14 | }
15 |
16 | body {
17 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
18 | background: linear-gradient(135deg, var(--background-color), #4834d4);
19 | color: var(--text-color);
20 | line-height: 1.6;
21 | min-height: 100vh;
22 | }
23 |
24 | .navbar {
25 | background-color: rgba(255, 255, 255, 0.1);
26 | backdrop-filter: blur(10px);
27 | padding: 1rem 2rem;
28 | display: flex;
29 | justify-content: space-between;
30 | align-items: center;
31 | position: fixed;
32 | height: 70px;
33 | width: 100%;
34 | top: 0;
35 | z-index: 1000;
36 | }
37 |
38 | .logo {
39 | font-size: 1.5rem;
40 | font-weight: bold;
41 | color: var(--text-color);
42 | }
43 |
44 | nav ul {
45 | list-style-type: none;
46 | display: flex;
47 | gap: 2rem;
48 | align-items: center;
49 | }
50 |
51 | .nav-link {
52 | color: var(--text-color);
53 | text-decoration: none;
54 | font-weight: bold;
55 | transition: color 0.3s ease, border-bottom 0.3s ease;
56 | padding-bottom: 0.25rem;
57 | }
58 |
59 | .nav-link:hover {
60 | color: var(--secondary-color);
61 | border-bottom: 2px solid var(--secondary-color);
62 | }
63 |
64 | .container {
65 | max-width: 1200px;
66 | margin: 0 auto;
67 | padding: 6rem 2rem 2rem;
68 | }
69 |
70 | .title {
71 | text-align: center;
72 | margin-bottom: 2rem;
73 | font-size: 2.5rem;
74 | color: var(--secondary-color);
75 | }
76 |
77 | .guilds-grid {
78 | display: grid;
79 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
80 | gap: 1.5rem;
81 | }
82 |
83 | .guild-card {
84 | background-color: var(--card-background);
85 | border-radius: 10px;
86 | padding: 1.5rem;
87 | box-shadow: var(--card-shadow);
88 | transition: transform 0.3s ease, box-shadow 0.3s ease;
89 | opacity: 0;
90 | transform: translateY(20px);
91 | animation: fadeInUp 0.5s ease forwards;
92 | }
93 |
94 | .guild-card:hover {
95 | transform: translateY(-5px);
96 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
97 | }
98 |
99 | .guild-header {
100 | display: flex;
101 | align-items: center;
102 | margin-bottom: 1rem;
103 | }
104 |
105 | .guild-icon {
106 | width: 64px;
107 | height: 64px;
108 | border-radius: 50%;
109 | margin-right: 1rem;
110 | object-fit: cover;
111 | }
112 |
113 | .guild-name {
114 | font-size: 1.2rem;
115 | font-weight: bold;
116 | color: var(--primary-color);
117 | }
118 |
119 | .guild-members {
120 | display: flex;
121 | align-items: center;
122 | color: var(--secondary-color);
123 | font-size: 1rem;
124 | }
125 |
126 | .guild-members::before {
127 | content: '\1F465';
128 | /* Unicode for 'busts in silhouette' emoji */
129 | margin-right: 0.5rem;
130 | font-size: 1.2rem;
131 | }
132 |
133 | @keyframes fadeInUp {
134 | to {
135 | opacity: 1;
136 | transform: translateY(0);
137 | }
138 | }
139 |
140 | @media (max-width: 768px) {
141 | .container {
142 | padding: 5rem 1rem 1rem;
143 | }
144 |
145 | .guilds-grid {
146 | grid-template-columns: 1fr;
147 | }
148 | }
--------------------------------------------------------------------------------
/admin/public/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: 'Arial', sans-serif;
5 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
6 | color: #ffffff;
7 | min-height: 100vh;
8 | display: flex;
9 | width: 96vw;
10 | flex-direction: column;
11 | }
12 |
13 |
14 | .banner {
15 | margin-top: 80px;
16 | text-align: center;
17 | }
18 |
19 | .banner-img {
20 | width: 103.2%; /* Make the banner image responsive */
21 | max-height: 300px; /* Adjust as needed */
22 | object-fit: cover; /* Cover the space nicely */
23 | }
24 |
25 | .container {
26 | max-width: 1200px;
27 | margin: 0 auto;
28 | padding: 2rem;
29 | flex: 1;
30 | }
31 |
32 | .features {
33 | display: grid;
34 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
35 | gap: 2rem;
36 | margin-top: 2rem; /* Reduce space above the features */
37 | }
38 |
39 | .feature-card {
40 | background-color: rgba(255, 255, 255, 0.1);
41 | border-radius: 10px;
42 | padding: 2rem;
43 | text-align: center;
44 | transition: transform 0.3s ease;
45 | }
46 |
47 | .feature-card:hover {
48 | transform: translateY(-5px);
49 | }
50 |
51 | .feature-card h3 {
52 | font-size: 1.5rem;
53 | margin-bottom: 1rem;
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/admin/views/botprofile.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | DiscoBase - Bot Information
9 |
10 |
11 |
12 |
13 |
14 |
DiscoBase
15 |
16 |
26 |
27 |
28 |
29 |
30 |
Bot Information
31 |
32 |
33 |
40 |
41 |
42 |
43 |
Bot ID
44 |
123456789
45 |
46 |
47 |
isVerified
48 |
Loading..
49 |
50 |
51 |
52 |
53 |
Update Bot Information
54 |
69 |
70 |
71 |
72 |
73 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/admin/views/commands.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | DiscoBase - Commands
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
DiscoBase
16 |
17 |
28 |
29 |
30 |
31 |
32 |
Commands
33 |
34 |
35 |
Slash Commands
36 |
37 |
38 |
39 |
40 |
Prefix Commands
41 |
42 |
43 |
44 |
45 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/admin/views/config.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | DiscoBase - Configuration Management
9 |
10 |
11 |
113 |
114 |
115 |
116 |
117 |
118 |
DiscoBase
119 |
120 |
132 |
133 |
134 |
135 |
136 |
Configuration Management
137 |
203 |
204 |
205 |
206 |
214 |
215 |
216 |
--------------------------------------------------------------------------------
/admin/views/errors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | DiscoBase - Error Logs
8 |
9 |
10 |
11 |
12 |
13 |
14 |
DiscoBase
15 |
16 |
26 |
27 |
28 |
29 |
30 |
Error Logs
31 |
32 |
33 |
34 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/admin/views/guilds.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | DiscoBase - Guilds
8 |
9 |
10 |
11 |
12 |
13 |
14 |
DiscoBase
15 |
16 |
25 |
26 |
27 |
28 |
29 |
Guilds
30 |
31 |
32 |
33 |
34 |
35 |
36 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/admin/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | DiscoBase - Dashboard
8 |
9 |
204 |
205 |
206 |
207 |
208 |
DiscoBase
209 |
210 |
225 |
226 |
227 |
228 |
229 |
Dashboard
230 |
231 |
232 |
233 |
Total Servers
234 |
0
235 |
236 |
237 |
Total Users
238 |
0
239 |
240 |
241 |
Total Commands (Prefix & Slash)
242 |
0
243 |
244 |
245 |
246 |
247 |
Recent Activity
248 |
251 |
252 |
253 |
254 |
307 |
308 |
309 |
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const fs = require('fs');
3 | const path = require('path');
4 | const { select, text, confirm } = require('@clack/prompts');
5 | const chalk = require('chalk');
6 |
7 | // Templates for different file types
8 | const templates = {
9 | command: `const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
10 |
11 | module.exports = {
12 | data: new SlashCommandBuilder()
13 | .setName('your-command')
14 | .setDescription('Describe your command here.'),
15 |
16 | async execute(interaction, client) {
17 | // Command execution logic goes here
18 | }
19 | };`,
20 | prefix: `//! This is a basic structure for a prefix command in discoBase using discord.js
21 |
22 | module.exports = {
23 | name: 'command-name',
24 | description: 'command-description.',
25 | aliases: ['alias_1', 'alias_2'],
26 | run: async (client, message, args) => {
27 | // Command execution logic goes here
28 | },
29 | };`,
30 | event: `module.exports = {
31 | name: 'event-name',
32 | async execute(eventObject, client) {
33 | // Event handling logic goes here
34 | }
35 | };`
36 | };
37 |
38 | // Logging function with styling
39 | const logWithStyle = (message, type = 'info') => {
40 | const styles = {
41 | success: chalk.green.bold(`✔ ${message}`),
42 | error: chalk.red.bold(`✖ ${message}`),
43 | info: chalk.blueBright.bold(`ℹ ${message}`),
44 | };
45 | console.log(styles[type] || message);
46 | };
47 |
48 | // Function to create a file with content
49 | const createFile = (filePath, template) => {
50 | fs.writeFile(filePath, template.trim(), (err) => {
51 | if (err) return logWithStyle(`Error: ${err.message}`, 'error');
52 | const relativePath = path.relative(path.join(__dirname, 'src'), filePath);
53 | logWithStyle(`File created at ${relativePath}`, 'success');
54 | });
55 | };
56 |
57 | // Main execution of the 'generate' command
58 | (async () => {
59 | // Ask for the file type
60 | const fileType = await select({
61 | message: 'Select the type of file to generate:',
62 | options: [
63 | { value: 'command', label: 'Command' },
64 | { value: 'event', label: 'Event' },
65 | { value: 'prefix', label: 'Prefix Command' }
66 | ],
67 | });
68 |
69 | const fileName = await text({
70 | message: `Enter the name of the ${fileType} file (without extension):`,
71 | initial: '',
72 | });
73 |
74 | const folderMap = {
75 | command: 'commands',
76 | event: 'events',
77 | prefix: 'messages'
78 | };
79 |
80 | const folderSelection = folderMap[fileType];
81 | const selectedFolderPath = path.join(__dirname, 'src', folderSelection);
82 |
83 | // Check if the folder exists, if not, ask to create it
84 | if (!fs.existsSync(selectedFolderPath)) {
85 | const createFolder = await confirm({
86 | message: `The folder ${folderSelection} does not exist. Do you want to create it?`,
87 | });
88 |
89 | if (createFolder) {
90 | fs.mkdirSync(selectedFolderPath, { recursive: true });
91 | logWithStyle(`Folder ${folderSelection} created successfully.`, 'success');
92 | } else {
93 | logWithStyle('Folder creation aborted.', 'error');
94 | return;
95 | }
96 | }
97 |
98 | // Get the subfolders within the selected folder
99 | let subFolders = fs.readdirSync(selectedFolderPath).filter(item => fs.statSync(path.join(selectedFolderPath, item)).isDirectory());
100 |
101 | // If no subfolders, ask if the user wants to create one
102 | if (subFolders.length === 0) {
103 | const createSubfolder = await confirm({
104 | message: `No subfolders exist in ${folderSelection}. Would you like to create one?`,
105 | });
106 |
107 | if (createSubfolder) {
108 | const subfolderName = await text({
109 | message: `Enter the name of the new subfolder:`,
110 | initial: '',
111 | });
112 |
113 | const newSubfolderPath = path.join(selectedFolderPath, subfolderName);
114 | fs.mkdirSync(newSubfolderPath, { recursive: true });
115 | logWithStyle(`Subfolder ${subfolderName} created successfully.`, 'success');
116 | subFolders = [subfolderName];
117 | } else {
118 | logWithStyle('Subfolder creation aborted.', 'error');
119 | return;
120 | }
121 | }
122 |
123 | // Let the user select an existing subfolder or create a new one
124 | const subfolderSelection = await select({
125 | message: 'Select the subfolder to create the file in (or choose to create a new folder):',
126 | options: [
127 | ...subFolders.map(subfolder => ({ value: subfolder, label: subfolder })),
128 | { value: 'new', label: 'Create new folder' }
129 | ]
130 | });
131 |
132 | let subfolderPath;
133 | if (subfolderSelection === 'new') {
134 | const newSubfolderName = await text({
135 | message: 'Enter the name of the new subfolder:',
136 | initial: '',
137 | });
138 | subfolderPath = path.join(selectedFolderPath, newSubfolderName);
139 | fs.mkdirSync(subfolderPath, { recursive: true });
140 | logWithStyle(`New subfolder ${newSubfolderName} created successfully.`, 'success');
141 | } else {
142 | subfolderPath = path.join(selectedFolderPath, subfolderSelection);
143 | }
144 |
145 | // Create the file
146 | const filePath = path.join(subfolderPath, `${fileName}.js`);
147 |
148 | if (fs.existsSync(filePath)) {
149 | logWithStyle(`File already exists: ${filePath}`, 'error');
150 | } else {
151 | createFile(filePath, templates[fileType]);
152 | }
153 | })();
154 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "bot": {
3 | "token": "YOUR_BOT_TOKEN",
4 | "id": "YOUR_BOT_ID",
5 | "admins": [
6 | "ADMIN_1",
7 | "ADMIN_2"
8 | ],
9 | "ownerId": "BOT_OWNER_ID",
10 | "developerCommandsServerIds": [
11 | "ONLY_DEV_COMMANDS_SERVER_IDS"
12 | ]
13 | },
14 | "database": {
15 | "mongodbUrl": "YOUR_MONGODB_URL"
16 | },
17 | "logging": {
18 | "guildJoinLogsId": "GUILD_JOIN_LOGS_CHANNEL_ID",
19 | "guildLeaveLogsId": "GUILD_LEAVE_LOGS_CHANNEL_ID",
20 | "commandLogsChannelId": "COMMAND_LOGS_CHANNEL_ID",
21 | "errorLogs": "YOUR_WEBHOOK_URL"
22 | },
23 | "prefix": {
24 | "value": "YOUR_BOT_PREFIX"
25 | }
26 | }
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to discoBase
2 |
3 | First of all, thank you for considering contributing to discoBase! Your support helps improve this package and make it better for everyone.
4 |
5 | ## How Can You Contribute?
6 |
7 | - Reporting bugs
8 | - Suggesting features
9 | - Submitting code improvements or new features
10 | - Improving documentation
11 |
12 | ## Guidelines for Contribution
13 |
14 | ### 1. Reporting Bugs
15 |
16 | If you find a bug, please report it by opening an [issue]([https://github.com/your-repo/discoBase/issues](https://github.com/ethical-programmer/discobase/issues)). Provide as much detail as possible:
17 |
18 | - Steps to reproduce the bug
19 | - Version of discoBase you're using
20 | - Any error logs
21 |
22 | ### 2. Suggesting Features
23 |
24 | Do you have an idea for a new feature? We'd love to hear it! Open an [issue]([https://github.com/your-repo/discoBase/issues](https://github.com/ethical-programmer/discobase/issues)) and explain the feature you'd like to see, along with any use cases.
25 |
26 | ### 3. Submitting Code Changes
27 |
28 | To submit a code change:
29 |
30 | 1. **Fork the repository** on GitHub.
31 | 2. **Clone your fork** locally:
32 | ```bash
33 | git clone https://github.com/ethical-programmer/discobase
34 | ```
35 | 3. **Create a new branch** for your changes:
36 | ```bash
37 | git checkout -b feature/your-feature
38 | ```
39 | 4. **Make your changes** to the codebase.
40 | 5. **Commit your changes**:
41 | ```bash
42 | git commit -m "Added a cool new feature"
43 | ```
44 | 6. **Push to your branch**:
45 | ```bash
46 | git push origin feature/your-feature
47 | ```
48 | 7. **Open a Pull Request** on GitHub.
49 |
50 | ### 4. Coding Standards
51 |
52 | - Use consistent formatting and indentation.
53 | - Write clear, concise commit messages.
54 | - Make sure your code is well-documented and includes comments where necessary.
55 |
56 | ### 5. Testing Your Changes
57 |
58 | Ensure that your changes do not introduce any errors or issues by thoroughly testing them before submitting. You can run the project locally to verify this.
59 |
60 | ### 6. Improving Documentation
61 |
62 | Even if you're not a developer, improving the documentation is a great way to contribute! You can submit fixes for typos or add missing sections that help other users understand the project better.
63 |
64 | ---
65 |
66 | Thank you for taking the time to contribute!
--------------------------------------------------------------------------------
/setup.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const { exec } = require('child_process');
6 | const { intro, outro, confirm, text, isCancel, spinner } = require('@clack/prompts');
7 | const chalk = require('chalk');
8 |
9 | function installPackages(packages, destination) {
10 | return new Promise((resolve, reject) => {
11 | const command = `npm install ${packages.join(' ')}`;
12 | exec(command, { cwd: destination }, (error, stdout, stderr) => {
13 | if (error) {
14 | console.error(chalk.red(`Error installing packages: ${stderr}`));
15 | reject(error);
16 | } else {
17 | console.log(chalk.green(`Packages installed: ${stdout}`));
18 | resolve();
19 | }
20 | });
21 | });
22 | }
23 |
24 | const excludeFiles = ['setup.js', 'package.json', 'package-lock.json', '.gitignore', 'README.md'];
25 | function copyProjectStructure(source, destination, includeDashboard) {
26 | if (!fs.existsSync(destination)) {
27 | fs.mkdirSync(destination, { recursive: true });
28 | }
29 |
30 | const items = fs.readdirSync(source);
31 |
32 | if (destination.endsWith('src')) {
33 | const schemasPath = path.join(destination, 'schemas');
34 | if (!fs.existsSync(schemasPath)) {
35 | fs.mkdirSync(schemasPath);
36 | }
37 | }
38 |
39 | items.forEach(item => {
40 | const srcPath = path.join(source, item);
41 | const destPath = path.join(destination, item);
42 |
43 | if (excludeFiles.includes(item)) {
44 | return;
45 | }
46 |
47 | if (!includeDashboard && item === 'admin') {
48 | return;
49 | }
50 |
51 | if (fs.lstatSync(srcPath).isDirectory()) {
52 | if (destination.endsWith('commands') || destination.endsWith('messages')) {
53 | const moderationPath = path.join(destination, 'Moderation');
54 | const otherPath = path.join(destination, 'Other');
55 |
56 | if (!fs.existsSync(moderationPath)) fs.mkdirSync(moderationPath);
57 | if (!fs.existsSync(otherPath)) fs.mkdirSync(otherPath);
58 | }
59 |
60 | if (destination.endsWith('events')) {
61 | const buttonEventPath = path.join(destination, 'buttons');
62 |
63 | if (!fs.existsSync(buttonEventPath)) fs.mkdirSync(buttonEventPath);
64 | }
65 |
66 | if (destination.endsWith('functions')) {
67 | const otherFunctionsPath = path.join(destination, 'other');
68 | if (!fs.existsSync(otherFunctionsPath)) fs.mkdirSync(otherFunctionsPath);
69 | }
70 |
71 | copyProjectStructure(srcPath, destPath, includeDashboard);
72 | } else {
73 | fs.copyFileSync(srcPath, destPath);
74 | }
75 | });
76 | }
77 |
78 | function createPackageJson(destination) {
79 | const packageJson = {
80 | name: path.basename(destination),
81 | main: "src/index.js",
82 | scripts: {
83 | "start": "node .",
84 | "generate": "node cli.js"
85 | },
86 |
87 | };
88 |
89 | fs.writeFileSync(path.join(destination, 'package.json'), JSON.stringify(packageJson, null, 2));
90 | }
91 |
92 | async function setupProjectStructure() {
93 | intro(chalk.yellowBright('Welcome to Discobase Setup!'));
94 |
95 | let projectName = await text({
96 | message: 'Enter your bot name (leave blank to create in the current directory):',
97 | validate(value) {
98 | return value.length > 100 ? 'Name too long' : undefined;
99 | }
100 | });
101 |
102 | if (isCancel(projectName)) {
103 | outro(chalk.red('Setup cancelled.'));
104 | return;
105 | }
106 |
107 | const installDependencies = await confirm({
108 | message: 'Do you want to install required dependencies for Discobase? [Recommended]',
109 | initialValue: true
110 | });
111 |
112 | if (isCancel(installDependencies)) {
113 | outro(chalk.red('Setup cancelled.'));
114 | return;
115 | }
116 |
117 | const installDiscord = await confirm({
118 | message: 'Do you want to install discord.js [Recommended]?',
119 | initialValue: true
120 | });
121 |
122 | if (isCancel(installDiscord)) {
123 | outro(chalk.red('Setup cancelled.'));
124 | return;
125 | }
126 |
127 | const installMongo = await confirm({
128 | message: 'Do you want to install MongoDB and Mongoose?',
129 | initialValue: true
130 | });
131 |
132 | if (isCancel(installMongo)) {
133 | outro(chalk.red('Setup cancelled.'));
134 | return;
135 | }
136 |
137 | const includeDashboard = await confirm({
138 | message: 'Do you want the Discobase Dashboard?',
139 | initialValue: true
140 | });
141 |
142 | if (isCancel(includeDashboard)) {
143 | outro(chalk.red('Setup cancelled.'));
144 | return;
145 | }
146 |
147 | const sourcePath = __dirname;
148 | const destinationPath = projectName ? path.join(process.cwd(), projectName) : process.cwd();
149 |
150 | if (projectName && !fs.existsSync(destinationPath)) {
151 | fs.mkdirSync(destinationPath);
152 | }
153 |
154 | process.chdir(destinationPath); // Change working directory to destinationPath
155 |
156 | const s = spinner();
157 | s.start(chalk.yellowBright('Copying project structure...'));
158 | copyProjectStructure(sourcePath, destinationPath, includeDashboard);
159 | createPackageJson(destinationPath);
160 | s.stop(chalk.green('Project structure copied successfully.'));
161 |
162 | const packagesToInstall = [];
163 | if (installDiscord) packagesToInstall.push('discord.js');
164 | if (installMongo) packagesToInstall.push('mongoose');
165 | if (installDependencies) packagesToInstall.push('chalk@4', 'chokidar', 'axios', '@clack/prompts', 'multer', 'express', 'set-interval-async', 'commander');
166 |
167 | if (packagesToInstall.length > 0) {
168 | console.log(chalk.yellow('Installing packages...'));
169 | try {
170 | await installPackages(packagesToInstall, destinationPath); // Pass destinationPath to installPackages
171 | console.log(chalk.green('Packages installed successfully.'));
172 | } catch (err) {
173 | console.error(chalk.red(err));
174 | }
175 | }
176 |
177 | outro(chalk.green('Discobase is installed successfully! Enjoy coding.'));
178 | }
179 |
180 | setupProjectStructure();
181 |
--------------------------------------------------------------------------------
/src/commands/Community/ping.js:
--------------------------------------------------------------------------------
1 | //! This is a basic structure for a slash command in a discoBase using discord.js
2 |
3 |
4 | const { SlashCommandBuilder } = require('discord.js');
5 |
6 | module.exports = {
7 | //! The 'data' property defines the slash command's structure using SlashCommandBuilder.
8 | data: new SlashCommandBuilder()
9 | //* Name of the slash command. In this case, the command will be '/ping'.
10 | .setName('ping')
11 |
12 | //* A short description of what the command does, shown when users type '/ping' in Discord.
13 | .setDescription('This is the ping command.'),
14 |
15 | //? Optional: Permissions that the bot requires to execute the command.
16 | //? botPermissions: ['SendMessages'], // Example: bot needs permission to send messages.
17 |
18 | //? Optional: Permissions that the user requires to use this command. Uncomment if needed.
19 | //? userPermissions: ['ManageMessages'], // Example: Only users with Manage Messages permission can use this command.
20 |
21 | //? Optional: Set this to true if only bot admins can use this command.
22 | //? adminOnly: true,
23 |
24 | //? Optional: Set this to true if only the bot owner can use this command.
25 | //? ownerOnly: true,
26 |
27 | //? Optional: Set this to true if only developers can use this command.
28 | //? devOnly: true, so if this true this slash command will only register for the server IDs you provided in config.json
29 |
30 | //? Optional: Cooldown period for the command in seconds to prevent spam.
31 | //? cooldown: 10,
32 |
33 | //! The 'execute' function is where the main logic for the command is placed.
34 | async execute(interaction, client) {
35 | try {
36 |
37 | const ping = Date.now() - interaction.createdTimestamp;
38 | const latency = Math.abs(ping);
39 | const latencyFormatted = `${latency.toString().substring(0, 2)}ms`;
40 | const emoji = "⏱️";
41 |
42 | await interaction.reply(`${emoji} Pong! Latency is ${latencyFormatted}!`);
43 | } catch (error) {
44 | console.error('An error occurred while executing the command:', error);
45 | }
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/src/events/handlers/guildJoinLogs.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder } = require('discord.js');
2 | const config = require('../../../config.json');
3 |
4 | module.exports = {
5 | name: 'guildCreate',
6 | async execute(guild, client) {
7 | const channelId = config.logging.guildJoinLogsId;
8 | if (channelId === 'GUILD_JOIN_LOGS_CHANNEL_ID') return;
9 | if (channelId) {
10 | const channel = client.channels.cache.get(channelId);
11 |
12 | if (channel) {
13 | const memberCount = guild.memberCount;
14 |
15 | const embed = new EmbedBuilder()
16 | .setColor('#0099ff')
17 | .setTitle(`Joined New Guild: ${guild.name}`)
18 | .addFields(
19 | { name: 'Total Members', value: `${memberCount}`, inline: true },
20 | { name: 'Guild ID', value: `${guild.id}`, inline: true },
21 | )
22 | .setTimestamp()
23 | .setFooter({ text: `Bot joined at` });
24 |
25 | if (guild.iconURL()) {
26 | embed.setThumbnail(guild.iconURL());
27 | }
28 |
29 | try {
30 | await channel.send({ embeds: [embed] });
31 | } catch (error) {
32 | console.error(`Failed to send message to channel ${channelId}:`, error);
33 | }
34 | } else {
35 | console.error(`Channel with ID ${channelId} does not exist or is not a text channel for guild join logs.`);
36 | }
37 | } else {
38 | console.error('No channel ID specified for guild join logs in config.');
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/events/handlers/guildLeaveLogs.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder } = require('discord.js');
2 | const config = require('../../../config.json');
3 |
4 | module.exports = {
5 | name: 'guildDelete',
6 | async execute(guild, client) {
7 | const channelId = config.logging.guildLeaveLogsId;
8 |
9 | if (channelId) {
10 | const channel = client.channels.cache.get(channelId);
11 |
12 | if (channel || channelId !== 'GUILD_LEAVE_LOGS_CHANNEL_ID') {
13 | const memberCount = guild.memberCount;
14 |
15 | const embed = new EmbedBuilder()
16 | .setColor('Red')
17 | .setTitle(`Leave Guild: ${guild.name}`)
18 | .addFields(
19 | { name: 'Total Members', value: `${memberCount}`, inline: true },
20 | { name: 'Guild ID', value: `${guild.id}`, inline: true },
21 | )
22 | .setTimestamp()
23 | .setFooter({ text: `Bot leaved at` });
24 |
25 | if (guild.iconURL()) {
26 | embed.setThumbnail(guild.iconURL());
27 | }
28 |
29 | try {
30 | await channel.send({ embeds: [embed] });
31 | } catch (error) {
32 | console.error(`Failed to send message to channel ${channelId}:`, error);
33 | }
34 | } else {
35 | console.error(`Channel with ID ${channelId} does not exist or is not a text channel for guild leave logs.`);
36 | }
37 | } else {
38 | console.error('No channel ID specified for guild leave logs in config.');
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/events/handlers/interactionCreate.js:
--------------------------------------------------------------------------------
1 | const { Interaction, Permissions, EmbedBuilder } = require("discord.js");
2 | const chalk = require("chalk");
3 | const config = require('../../../config.json');
4 | const path = require('path');
5 | const fs = require('fs');
6 |
7 | const errorsDir = path.join(__dirname, '../../../errors'); // Ensure correct path to the root
8 |
9 | // Function to create the errors directory if it doesn't exist
10 | function ensureErrorDirectoryExists() {
11 | if (!fs.existsSync(errorsDir)) {
12 | fs.mkdirSync(errorsDir);
13 | }
14 | }
15 |
16 | // Function to log errors to a file
17 | function logErrorToFile(error) {
18 | ensureErrorDirectoryExists();
19 |
20 | // Convert the error object into a string, including the stack trace
21 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
22 |
23 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
24 | const filePath = path.join(errorsDir, fileName);
25 |
26 | fs.writeFileSync(filePath, errorMessage, 'utf8');
27 | }
28 |
29 |
30 | module.exports = {
31 | name: 'interactionCreate',
32 | async execute(interaction, client) {
33 |
34 | if (!interaction.isCommand()) return;
35 |
36 | const command = client.commands.get(interaction.commandName);
37 |
38 | if (!command) {
39 | console.log(chalk.yellow(`Command "${interaction.commandName}" not found.`));
40 | return;
41 | }
42 |
43 | if (command.adminOnly) {
44 | if (!config.bot.admins.includes(interaction.user.id)) {
45 |
46 | const embed = new EmbedBuilder()
47 | .setColor('Blue')
48 | .setDescription(`\`❌\` | This command is admin-only. You cannot run this command.`)
49 |
50 | return await interaction.reply({
51 | embeds: [embed],
52 | ephemeral: true
53 | });
54 | }
55 | }
56 |
57 | if (command.ownerOnly) {
58 | if (interaction.user.id !== config.bot.ownerId) {
59 | const embed = new EmbedBuilder()
60 | .setColor('Blue')
61 | .setDescription(`\`❌\` | This command is owner-only. You cannot run this command.`)
62 |
63 | return await interaction.reply({
64 | embeds: [embed],
65 | ephemeral: true
66 | });
67 | }
68 | }
69 |
70 | if (command.userPermissions) {
71 | const memberPermissions = interaction.member.permissions;
72 | const missingPermissions = command.userPermissions.filter(perm => !memberPermissions.has(perm));
73 |
74 | if (missingPermissions.length) {
75 |
76 | const embed = new EmbedBuilder()
77 | .setColor('Blue')
78 | .setDescription(`\`❌\` | You lack the necessary permissions to execute this command: \`\`\`${missingPermissions.join(", ")}\`\`\``)
79 |
80 | return await interaction.reply({
81 | embeds: [embed],
82 | ephemeral: true
83 | });
84 | }
85 | }
86 |
87 |
88 |
89 | if (command.botPermissions) {
90 | const botPermissions = interaction.guild.members.me.permissions;
91 | const missingBotPermissions = command.botPermissions.filter(perm => !botPermissions.has(perm));
92 | if (missingBotPermissions.length) {
93 |
94 |
95 | const embed = new EmbedBuilder()
96 | .setColor('Blue')
97 | .setDescription(`\`❌\` | I lack the necessary permissions to execute this command: \`\`\`${missingBotPermissions.join(", ")}\`\`\``)
98 |
99 | return await interaction.reply({
100 | embeds: [embed],
101 | ephemeral: true
102 | });
103 | }
104 | }
105 |
106 | const cooldowns = client.cooldowns || new Map();
107 | const now = Date.now();
108 | const cooldownAmount = (command.cooldown || 3) * 1000;
109 | const timestamps = cooldowns.get(command.name) || new Map();
110 |
111 | if (timestamps.has(interaction.user.id)) {
112 | const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount;
113 |
114 | if (now < expirationTime) {
115 | const timeLeft = (expirationTime - now) / 1000;
116 |
117 | const embed = new EmbedBuilder()
118 | .setColor('Blue')
119 | .setDescription(`\`❌\` | Please wait **${timeLeft.toFixed(1)}** more second(s) before reusing the command.`)
120 |
121 | return await interaction.reply({
122 | embeds: [embed],
123 | ephemeral: true
124 | });
125 | }
126 | }
127 |
128 | timestamps.set(interaction.user.id, now);
129 | cooldowns.set(command.name, timestamps);
130 |
131 | try {
132 | await command.execute(interaction, client);
133 | // Create an embed to log the command execution
134 | const logEmbed = new EmbedBuilder()
135 | .setColor('#0099ff')
136 | .setTitle('Command Executed')
137 | .addFields(
138 | { name: 'User', value: `${ interaction.user.tag }(${ interaction.user.id })`, inline: true },
139 | { name: 'Command', value: `/ ${ command.data.name }`, inline: true },
140 | { name: 'Server', value: `${ interaction.guild.name }(${ interaction.guild.id })`, inline: true },
141 | { name: 'Timestamp', value: new Date().toLocaleString(), inline: true }
142 | )
143 | .setTimestamp();
144 |
145 | // Send the embed to the specified logs channel
146 | if (config.logging.commandLogsChannelId) {
147 | if (config.logging.commandLogsChannelId === 'COMMAND_LOGS_CHANNEL_ID') return;
148 | const logsChannel = client.channels.cache.get(config.logging.commandLogsChannelId);
149 | if (logsChannel) {
150 | await logsChannel.send({ embeds: [logEmbed] });
151 | } else {
152 | console.error(chalk.yellow(`Logs channel with ID ${ config.logging.commandLogsChannelId } not found.`));
153 | }
154 | }
155 | } catch (error) {
156 | console.error(chalk.red(`Error executing command "${command.data.name}": `), error);
157 | await interaction.reply({
158 | content: 'There was an error while executing this command!',
159 | ephemeral: true
160 | });
161 | logErrorToFile(error)
162 |
163 | }
164 | },
165 | };
166 |
--------------------------------------------------------------------------------
/src/events/handlers/prefixCreate.js:
--------------------------------------------------------------------------------
1 | const chalk = require("chalk");
2 | const config = require('../../../config.json');
3 | const { EmbedBuilder } = require('discord.js');
4 | const { getSimilarCommands } = require('../../functions/handlers/similarity');
5 | const path = require('path');
6 | const fs = require('fs');
7 |
8 | const errorsDir = path.join(__dirname, '../../../errors');
9 |
10 | function ensureErrorDirectoryExists() {
11 | if (!fs.existsSync(errorsDir)) {
12 | fs.mkdirSync(errorsDir);
13 | }
14 | }
15 |
16 | function logErrorToFile(error) {
17 | ensureErrorDirectoryExists();
18 |
19 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
20 |
21 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
22 | const filePath = path.join(errorsDir, fileName);
23 |
24 | fs.writeFileSync(filePath, errorMessage, 'utf8');
25 | }
26 |
27 |
28 | module.exports = {
29 | name: 'messageCreate',
30 | async execute(message, client) {
31 | const prefix = config.prefix.value;
32 | const content = message.content.toLowerCase();
33 | if (prefix === '') return;
34 | if (!content.startsWith(prefix) || message.author.bot) return;
35 |
36 | const args = content.slice(prefix.length).trim().split(/ +/);
37 | const commandName = args.shift().toLowerCase();
38 |
39 | let command = client.prefix.get(commandName);
40 | if (!command) {
41 | command = Array.from(client.prefix.values()).find(
42 | (cmd) => cmd.aliases && cmd.aliases.includes(commandName)
43 | );
44 | }
45 |
46 | if (!command) {
47 | console.log(chalk.yellow.bold('WARNING: ') + `Unknown command: "${commandName}"`);
48 |
49 | const similarCommands = getSimilarCommands(commandName, Array.from(client.prefix.values()));
50 | if (similarCommands.length > 0) {
51 | const embed = new EmbedBuilder()
52 | .setColor('Blue')
53 | .setDescription(`\`🤔\` | Command not found. Did you mean: ${similarCommands.join(', ')}?`)
54 |
55 | return await message.reply({ embeds: [embed] });
56 | } else {
57 | return;
58 | }
59 | }
60 |
61 |
62 | if (command.devOnly) {
63 | if (!config.bot.developerCommandsServerIds.includes(message.guild.id)) {
64 | return;
65 | }
66 | }
67 |
68 |
69 | if (!client.cooldowns) {
70 | client.cooldowns = new Map();
71 | }
72 |
73 | const now = Date.now();
74 | const cooldownAmount = (command.cooldown || 3) * 1000;
75 |
76 | if (!client.cooldowns.has(command.name)) {
77 | client.cooldowns.set(command.name, new Map());
78 | }
79 |
80 | const timestamps = client.cooldowns.get(command.name);
81 |
82 | if (timestamps.has(message.author.id)) {
83 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount;
84 | if (now < expirationTime) {
85 | const timeLeft = (expirationTime - now) / 1000;
86 |
87 | const embed = new EmbedBuilder()
88 | .setColor('Blue')
89 | .setDescription(`\`❌\` | Please wait **${timeLeft.toFixed(1)}** more second(s) before reusing the \`${command.name}\` command.`)
90 |
91 | return message.reply({
92 | embeds: [embed]
93 | });
94 | }
95 | }
96 |
97 | timestamps.set(message.author.id, now);
98 |
99 | if (command.adminOnly && !config.bot.admins.includes(message.author.id)) {
100 | const embed = new EmbedBuilder()
101 | .setColor('Blue')
102 | .setDescription(`\`❌\` | This command is admin-only. You cannot run this command.`)
103 |
104 | return message.reply({
105 | embeds: [embed]
106 | });
107 | }
108 |
109 |
110 | if (command.ownerOnly && message.author.id !== config.bot.ownerId) {
111 | const embed = new EmbedBuilder()
112 | .setColor('Blue')
113 | .setDescription(`\`❌\` | This command is owner-only. You cannot run this command.`)
114 |
115 | return await message.reply({
116 | embeds: [embed],
117 | });
118 | }
119 |
120 | if (command.userPermissions) {
121 | const memberPermissions = message.member.permissions;
122 | const missingPermissions = command.userPermissions.filter(perm => !memberPermissions.has(perm));
123 | if (missingPermissions.length) {
124 | const embed = new EmbedBuilder()
125 | .setColor('Blue')
126 | .setDescription(`\`❌\` | You lack the necessary permissions to execute this command: \`\`\`${missingPermissions.join(", ")}\`\`\``)
127 |
128 | return message.reply({
129 | embeds: [embed],
130 | });
131 | }
132 | }
133 |
134 | if (command.botPermissions) {
135 | const botPermissions = message.guild.members.me.permissions;
136 | const missingBotPermissions = command.botPermissions.filter(perm => !botPermissions.has(perm));
137 | if (missingBotPermissions.length) {
138 | const embed = new EmbedBuilder()
139 | .setColor('Blue')
140 | .setDescription(`\`❌\` | I lack the necessary permissions to execute this command: \`\`\`${missingBotPermissions.join(", ")}\`\`\``)
141 |
142 | return message.reply({
143 | embeds: [embed],
144 | });
145 | }
146 | }
147 |
148 | try {
149 | await command.run(client, message, args);
150 | const logEmbed = new EmbedBuilder()
151 | .setColor('Blue')
152 | .setTitle('Command Executed')
153 | .addFields(
154 | { name: 'User', value: `${message.author.tag} (${message.author.id})`, inline: true },
155 | { name: 'Command', value: `${config.prefix.value}${command.name}`, inline: true },
156 | { name: 'Server', value: `${message.guild.name} (${message.guild.id})`, inline: true },
157 | { name: 'Timestamp', value: new Date().toLocaleString(), inline: true }
158 | )
159 | .setTimestamp();
160 |
161 | if (config.logging.commandLogsChannelId) {
162 | const logsChannel = client.channels.cache.get(config.logging.commandLogsChannelId);
163 | if (logsChannel) {
164 | await logsChannel.send({ embeds: [logEmbed] });
165 | } else {
166 | if (config.logging.commandLogsChannelId === 'COMMAND_LOGS_CHANNEL_ID') return;
167 |
168 | console.error(chalk.yellow(`Logs channel with ID ${config.logging.commandLogsChannelId} not found.`));
169 | }
170 | }
171 | } catch (error) {
172 | console.log(chalk.red.bold('ERROR: ') + `Failed to execute command "${commandName}".`);
173 | console.error(error);
174 | message.reply({
175 | content: 'There was an error while executing this command!',
176 | });
177 | logErrorToFile(error)
178 |
179 | }
180 | }
181 | };
182 |
--------------------------------------------------------------------------------
/src/events/handlers/ready.js:
--------------------------------------------------------------------------------
1 | const config = require('../../../config.json');
2 | const mongoose = require('mongoose');
3 | const chalk = require('chalk');
4 | const { ActivityType } = require('discord.js');
5 | const { prefixHandler } = require('../../functions/handlers/prefixHandler');
6 | const { handleCommands } = require('../../functions/handlers/handleCommands');
7 | const path = require('path');
8 | const mongodbURL = config.database.mongodbUrl;
9 | const fs = require('fs')
10 |
11 | const errorsDir = path.join(__dirname, '../../../errors');
12 | function ensureErrorDirectoryExists() {
13 | if (!fs.existsSync(errorsDir)) {
14 | fs.mkdirSync(errorsDir);
15 | }
16 | }
17 |
18 | function logErrorToFile(error) {
19 | ensureErrorDirectoryExists();
20 |
21 | // Convert the error object into a string, including the stack trace
22 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
23 |
24 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
25 | const filePath = path.join(errorsDir, fileName);
26 |
27 | fs.writeFileSync(filePath, errorMessage, 'utf8');
28 | }
29 |
30 |
31 | module.exports = {
32 | name: 'ready',
33 | once: true,
34 | async execute(client) {
35 | console.log(chalk.green.bold('INFO: ') + 'Bot is ready and connected to Discord!');
36 |
37 | if (!mongodbURL || mongodbURL === 'YOUR_MONGODB_URL') {
38 | console.log(chalk.yellow.bold('INFO: ') + 'MongoDB URL is not provided or is set to the default placeholder. Skipping MongoDB connection.');
39 | } else {
40 | try {
41 | await mongoose.connect(mongodbURL);
42 | if (mongoose.connect) {
43 | console.log(chalk.green.bold('SUCCESS: ') + 'Connected to MongoDB successfully!');
44 | }
45 | } catch (error) {
46 | console.log(chalk.red.bold('ERROR: ') + 'Failed to connect to MongoDB. Please check your MongoDB URL and connection.');
47 | console.error(error);
48 | logErrorToFile(error);
49 |
50 | }
51 | }
52 |
53 |
54 |
55 | client.user.setPresence({
56 | activities: [{
57 | type: ActivityType.Custom,
58 | name: "custom",
59 | state: "🚀 discobase!"
60 | }]
61 | })
62 | prefixHandler(client, path.join(process.cwd(), 'src/messages'));
63 | handleCommands(client, path.join(process.cwd(), 'src/commands'));
64 | },
65 | };
66 |
--------------------------------------------------------------------------------
/src/functions/handlers/antiCrash.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 | const config = require('../../../config.json');
3 | const chalk = require('chalk');
4 | const process = require('node:process');
5 | const fs = require('fs');
6 | const path = require('path');
7 |
8 | function antiCrash() {
9 | const webhookURL = config.logging.errorLogs;
10 | const errorsDir = path.join(__dirname, '../../../errors');
11 |
12 | function ensureErrorDirectoryExists() {
13 | if (!fs.existsSync(errorsDir)) {
14 | fs.mkdirSync(errorsDir);
15 | }
16 | }
17 |
18 | function logErrorToFile(error) {
19 | ensureErrorDirectoryExists();
20 |
21 | // Convert the error object into a string, including the stack trace
22 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
23 |
24 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
25 | const filePath = path.join(errorsDir, fileName);
26 |
27 | fs.writeFileSync(filePath, errorMessage, 'utf8');
28 | }
29 |
30 |
31 | async function sendErrorNotification(message) {
32 | if (!webhookURL || webhookURL === "YOUR_DISCORD_WEBHOOK_URL") {
33 | console.warn(chalk.yellow.bold('WARNING:') + ' No valid webhook URL provided. Unable to send error notifications.');
34 | return;
35 | }
36 |
37 | const embed = {
38 | title: "Error Notification",
39 | description: message,
40 | color: 0xff0000,
41 | timestamp: new Date(),
42 | footer: {
43 | text: "Bot Error Logger",
44 | },
45 | };
46 |
47 | await axios.post(webhookURL, { embeds: [embed] })
48 | .catch(error => {
49 | console.warn(chalk.yellow.bold('WARNING:') + ' Failed to send error notification:', error.message);
50 | });
51 | }
52 |
53 | process.on('unhandledRejection', async (reason, promise) => {
54 | const errorMessage = reason.message.includes("Used disallowed intents")
55 | ? 'Used disallowed intents. Please check your bot settings on the Discord developer portal.'
56 | : `Unhandled Rejection at: ${promise} \nReason: ${reason} \nStack: ${reason.stack || 'No stack trace available.'}`;
57 |
58 | console.error(chalk.red.bold('ERROR:') + ' ' + errorMessage);
59 |
60 | logErrorToFile(errorMessage);
61 |
62 | await sendErrorNotification(errorMessage);
63 | });
64 |
65 | process.on('uncaughtException', async (error) => {
66 | const errorMessage = error.message.includes("Used disallowed intents")
67 | ? 'Used disallowed intents. Please check your bot settings on the Discord developer portal.'
68 | : `Uncaught Exception: ${error.message} \nStack: ${error.stack || 'No stack trace available.'}`;
69 |
70 | console.error(chalk.red.bold('ERROR:') + ' ' + errorMessage);
71 |
72 | logErrorToFile(errorMessage);
73 |
74 | await sendErrorNotification(errorMessage);
75 | });
76 | }
77 |
78 | module.exports = { antiCrash };
79 |
--------------------------------------------------------------------------------
/src/functions/handlers/functionHandler.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const { setIntervalAsync, clearIntervalAsync } = require('set-interval-async/fixed');
4 | let intervalId;
5 |
6 | const functionsDir = path.join(__dirname, '../');
7 |
8 | const getAllFunctionFiles = (dir) => {
9 | let results = [];
10 |
11 | const list = fs.readdirSync(dir);
12 | list.forEach(file => {
13 | const filePath = path.join(dir, file);
14 | const stat = fs.statSync(filePath);
15 |
16 | if (stat && stat.isDirectory()) {
17 | results = results.concat(getAllFunctionFiles(filePath));
18 | } else if (file.endsWith('.js')) {
19 | results.push(filePath);
20 | }
21 | });
22 |
23 | return results;
24 | };
25 |
26 | const loadFunctions = () => {
27 | const functionFiles = getAllFunctionFiles(functionsDir);
28 | return functionFiles.map(file => require(file));
29 | };
30 |
31 | const handleFunction = async (func) => {
32 | if (typeof func === 'function') {
33 | const config = func.config || {};
34 | await handleSingleFunction(func.name, func, config);
35 | }
36 | };
37 |
38 | const handleSingleFunction = async (name, func, config) => {
39 |
40 | const { once, interval, retryAttempts, maxExecution, initializer } = config;
41 |
42 | if (interval && isNaN(interval)) {
43 | console.error(`Invalid interval for function ${name}. Interval must be a number.`);
44 | return;
45 | }
46 |
47 | if (initializer) {
48 | console.log(`Waiting ${initializer} seconds before starting function.`);
49 | await new Promise(resolve => setTimeout(resolve, initializer * 1000));
50 | }
51 |
52 | let executions = 0;
53 | let retries = 0;
54 |
55 | const runFunction = async () => {
56 | if (executions >= maxExecution) {
57 | console.log(`Max executions reached for ${name}.`);
58 | return clearIntervalAsync(intervalId);
59 | }
60 |
61 | try {
62 | console.log(`Executing function: ${name}...`);
63 | await func();
64 | executions++;
65 | } catch (error) {
66 | console.error(`Error executing function ${name}, Retrying...`);
67 | retries++;
68 | if (retries >= retryAttempts) {
69 | console.log(`Failed after ${retryAttempts} retries for ${name}.`);
70 | return clearIntervalAsync(intervalId);
71 | }
72 | }
73 | };
74 |
75 | if (once) {
76 | await runFunction();
77 | } else if (interval) {
78 | intervalId = setIntervalAsync(runFunction, interval);
79 | }
80 | };
81 |
82 | const runHandlers = async () => {
83 | const functions = loadFunctions();
84 | console.log(`Loaded ${functions.length} functions.`);
85 |
86 | for (const func of functions) {
87 | await handleFunction(func);
88 | }
89 | };
90 |
91 | runHandlers().catch(console.error);
92 |
--------------------------------------------------------------------------------
/src/functions/handlers/handelEvents.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const { Collection } = require('discord.js');
6 | const chokidar = require('chokidar');
7 | const chalk = require('chalk');
8 |
9 | const debounce = (func, delay) => {
10 | let timeoutId;
11 | return (...args) => {
12 | clearTimeout(timeoutId);
13 | timeoutId = setTimeout(() => {
14 | func.apply(null, args);
15 | }, delay);
16 | };
17 | };
18 |
19 | const errorsDir = path.join(__dirname, '../../../errors');
20 |
21 | function ensureErrorDirectoryExists() {
22 | if (!fs.existsSync(errorsDir)) {
23 | fs.mkdirSync(errorsDir);
24 | }
25 | }
26 |
27 | function logErrorToFile(error) {
28 | ensureErrorDirectoryExists();
29 |
30 | // Convert the error object into a string, including the stack trace
31 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
32 |
33 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
34 | const filePath = path.join(errorsDir, fileName);
35 |
36 | fs.writeFileSync(filePath, errorMessage, 'utf8');
37 | }
38 |
39 |
40 | const getShortPath = (filePath) => path.basename(filePath);
41 |
42 | const eventsHandler = async (client, eventsPath) => {
43 | client.events = new Collection();
44 |
45 | const getFilesRecursively = (dir) => {
46 | let files = [];
47 | const items = fs.readdirSync(dir);
48 |
49 | for (const item of items) {
50 | const fullPath = path.join(dir, item);
51 | if (fs.statSync(fullPath).isDirectory()) {
52 | files = [...files, ...getFilesRecursively(fullPath)];
53 | } else if (item.endsWith('.js')) {
54 | files.push(fullPath);
55 | }
56 | }
57 |
58 | return files;
59 | };
60 |
61 | const loadEvent = (file) => {
62 | try {
63 | delete require.cache[require.resolve(file)];
64 | const event = require(file);
65 |
66 | if (event.name) {
67 | client.events.set(event.name, event);
68 | if (event.once) {
69 | client.once(event.name, (...args) => event.execute(...args, client));
70 | } else {
71 | client.on(event.name, (...args) => event.execute(...args, client));
72 | }
73 |
74 | console.log(
75 | chalk.green.bold('SUCCESS: ') +
76 | `Loaded event: ${chalk.cyan.bold(event.name)} from ${chalk.yellow(getShortPath(file))}`
77 | );
78 | } else {
79 | console.warn(chalk.yellow.bold('WARNING: ') + `File ${chalk.yellow(getShortPath(file))} does not export a valid event name.`);
80 | }
81 | } catch (error) {
82 | console.error(chalk.red.bold('ERROR: ') + `Failed to load event from ${chalk.yellow(getShortPath(file))}:`, error);
83 | logErrorToFile(error)
84 | }
85 | };
86 |
87 | const loadSchema = (file) => {
88 | try {
89 | delete require.cache[require.resolve(file)];
90 | const schema = require(file);
91 |
92 | console.log(chalk.green.bold('SUCCESS: ') + `Loaded schema from ${chalk.yellow(getShortPath(file))}`);
93 | } catch (error) {
94 | console.error(chalk.red.bold('ERROR: ') + `Failed to load schema from ${chalk.yellow(getShortPath(file))}:`, error);
95 | logErrorToFile(error)
96 | }
97 | };
98 |
99 | const unloadEvent = (file) => {
100 | const event = require(file);
101 | if (event.name && client.events.has(event.name)) {
102 | client.removeAllListeners(event.name);
103 | client.events.delete(event.name);
104 | console.log(`Unloaded event: ${event.name}`);
105 | } else {
106 | console.log(
107 | chalk.yellow.bold('WARNING: ') +
108 | `Event "${chalk.red(getShortPath(file))}" not found in client collection.`
109 | );
110 | }
111 | };
112 |
113 | const loadAllEvents = (eventDir) => {
114 | const eventFiles = getFilesRecursively(eventDir);
115 | eventFiles.forEach(file => loadEvent(file));
116 | };
117 |
118 | const loadAllSchemas = (schemasDir) => {
119 | const schemaFiles = getFilesRecursively(schemasDir);
120 | schemaFiles.forEach(file => loadSchema(file));
121 | };
122 |
123 | loadAllEvents(eventsPath);
124 | loadAllSchemas(path.join(__dirname, '../../schemas'));
125 |
126 | const watcher = chokidar.watch([eventsPath, path.join(__dirname, '../../schemas')], {
127 | persistent: true,
128 | ignoreInitial: true,
129 | awaitWriteFinish: true,
130 | ignored: [
131 | path.join(__dirname, '../../functions/**'),
132 | ],
133 | });
134 |
135 | watcher
136 | .on('add', (filePath) => {
137 | if (filePath.endsWith('.js')) {
138 | console.log(chalk.blue.bold('WATCHER: ') + `New file added: ${chalk.yellow.bold(getShortPath(filePath))}`);
139 | if (filePath.includes('schemas')) {
140 | loadSchema(filePath);
141 | } else {
142 | loadEvent(filePath);
143 | }
144 | }
145 | })
146 | .on('change', (filePath) => {
147 | console.log(chalk.blue.bold('WATCHER: ') + `File changed: ${chalk.yellow.bold(getShortPath(filePath))}`);
148 | if (filePath.includes('schemas')) {
149 | loadSchema(filePath);
150 | } else {
151 | unloadEvent(filePath);
152 | loadEvent(filePath);
153 | }
154 | })
155 | .on('unlink', (filePath) => {
156 | console.log(chalk.blue.bold('WATCHER: ') + `File removed: ${chalk.yellow.bold(getShortPath(filePath))}`);
157 | if (filePath.includes('schemas')) {
158 | } else {
159 | unloadEvent(filePath);
160 | }
161 | });
162 | };
163 |
164 |
165 |
166 | module.exports = { eventsHandler };
167 |
--------------------------------------------------------------------------------
/src/functions/handlers/handleCommands.js:
--------------------------------------------------------------------------------
1 |
2 | const { REST, Routes, Collection } = require('discord.js');
3 | const fs = require('fs');
4 | const chalk = require('chalk');
5 | const config = require('../../../config.json');
6 | const path = require('path');
7 | const chokidar = require('chokidar');
8 | const activities = [];
9 | const addActivity = (action, filePath) => {
10 | const timestamp = new Date().toISOString();
11 | activities.push({ action, filePath, timestamp });
12 | };
13 |
14 | const getActivities = () => activities;
15 |
16 |
17 | const log = (message, type = 'INFO') => {
18 | const colors = {
19 | INFO: chalk.blue.bold('INFO:'),
20 | SUCCESS: chalk.green.bold('SUCCESS:'),
21 | ERROR: chalk.red.bold('ERROR:'),
22 | WARNING: chalk.yellow.bold('WARNING:')
23 | };
24 | console.log(colors[type] + ' ' + message);
25 | };
26 |
27 | const errorsDir = path.join(__dirname, '../../../errors');
28 |
29 | function ensureErrorDirectoryExists() {
30 | if (!fs.existsSync(errorsDir)) {
31 | fs.mkdirSync(errorsDir);
32 | }
33 | }
34 |
35 | function logErrorToFile(error) {
36 | ensureErrorDirectoryExists();
37 |
38 | // Convert the error object into a string, including the stack trace
39 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
40 |
41 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
42 | const filePath = path.join(errorsDir, fileName);
43 |
44 | fs.writeFileSync(filePath, errorMessage, 'utf8');
45 | }
46 |
47 |
48 | const formatFilePath = (filePath) => {
49 | return path.relative(process.cwd(), filePath);
50 | };
51 |
52 | const isConfigIncomplete = (key, value, placeholderTokens) => {
53 | return !value || placeholderTokens.includes(value);
54 | };
55 |
56 | const getAllCommandFiles = (dirPath, arrayOfFiles = []) => {
57 | const files = fs.readdirSync(dirPath);
58 | files.forEach(file => {
59 | const filePath = path.join(dirPath, file);
60 | if (fs.statSync(filePath).isDirectory()) {
61 | arrayOfFiles = getAllCommandFiles(filePath, arrayOfFiles);
62 | } else if (file.endsWith('.js')) {
63 | arrayOfFiles.push(filePath);
64 | }
65 | });
66 | return arrayOfFiles;
67 | };
68 |
69 | const loadCommand = (client, filePath) => {
70 | try {
71 | if (filePath.includes('schemas')) {
72 | log(`Ignoring schema file: ${formatFilePath(filePath)}`, 'WARNING');
73 | return null;
74 | }
75 |
76 | delete require.cache[require.resolve(filePath)];
77 | const command = require(filePath);
78 |
79 | if (!command.data || !command.data.name || typeof command.data.name !== 'string') {
80 | log(`The command file "${formatFilePath(filePath)}" is missing a valid name property.`, 'ERROR');
81 | return null;
82 | }
83 |
84 | client.commands.set(command.data.name, command);
85 | return command;
86 |
87 | } catch (error) {
88 | log(`Failed to load command from "${formatFilePath(filePath)}".`, 'ERROR');
89 | console.error(error);
90 | logErrorToFile(error)
91 | return null;
92 | }
93 | };
94 |
95 | const loadCommands = (client, commandsPath) => {
96 | const globalCommandArray = [];
97 | const devCommandArray = [];
98 |
99 | const commandFiles = getAllCommandFiles(commandsPath);
100 |
101 | for (const filePath of commandFiles) {
102 | const command = loadCommand(client, filePath);
103 | if (command) {
104 | if (command.devOnly) {
105 | devCommandArray.push(command.data.toJSON());
106 | } else {
107 | globalCommandArray.push(command.data.toJSON());
108 | }
109 | }
110 | }
111 |
112 | return { globalCommandArray, devCommandArray };
113 | };
114 |
115 | const unregisterCommand = async (commandName, rest, config, devCommandArray) => {
116 | try {
117 | log(`Unregistering global command: ${commandName}`, 'INFO');
118 |
119 |
120 |
121 | const globalCommands = await rest.get(Routes.applicationCommands(config.bot.id));
122 | const commandToDelete = globalCommands.find(cmd => cmd.name === commandName);
123 | if (commandToDelete) {
124 | await rest.delete(Routes.applicationCommand(config.bot.id, commandToDelete.id));
125 | log(`Successfully unregistered global command: ${commandName}`, 'SUCCESS');
126 | }
127 |
128 |
129 | if (devCommandArray.length > 0 && config.bot.developerCommandsServerIds && config.bot.developerCommandsServerIds.length > 0) {
130 | for (const serverId of config.bot.developerCommandsServerIds) {
131 | const guildCommands = await rest.get(Routes.applicationGuildCommands(config.bot.id, serverId));
132 | const guildCommandToDelete = guildCommands.find(cmd => cmd.name === commandName);
133 | if (guildCommandToDelete) {
134 | await rest.delete(Routes.applicationGuildCommand(config.bot.id, serverId, guildCommandToDelete.id));
135 | log(`Successfully unregistered command: ${commandName} from guild ${serverId}`, 'SUCCESS');
136 | }
137 | }
138 | }
139 | } catch (error) {
140 | log(`Failed to unregister command: ${commandName}`, 'ERROR');
141 | console.error(error);
142 | logErrorToFile(error)
143 | }
144 | };
145 |
146 | const registerCommands = async (globalCommandArray, devCommandArray, rest, config) => {
147 | if (globalCommandArray.length > 0) {
148 | try {
149 | log('Started refreshing global application (/) commands.', 'INFO');
150 | await rest.put(
151 | Routes.applicationCommands(config.bot.id),
152 | { body: globalCommandArray }
153 | );
154 | log('Successfully reloaded global application (/) commands.', 'SUCCESS');
155 | } catch (error) {
156 | log('Failed to reload global application (/) commands.', 'ERROR');
157 | if (error.code === 10002) {
158 | console.error(chalk.red.bold('ERROR: ') + 'Unknown Application. Please check the Discord bot ID provided in your configuration.');
159 | logErrorToFile(error)
160 | } else {
161 | console.error(chalk.red.bold('ERROR: ') + 'Failed to register commands:', error.message);
162 | logErrorToFile(error)
163 | }
164 | }
165 | }
166 |
167 | if (devCommandArray.length > 0 && config.bot.developerCommandsServerIds && config.bot.developerCommandsServerIds.length > 0) {
168 | const promises = config.bot.developerCommandsServerIds.map(async (serverId) => {
169 | try {
170 | log(`Started refreshing developer guild (/) commands for server: ${serverId}`, 'INFO');
171 | await rest.put(
172 | Routes.applicationGuildCommands(config.bot.id, serverId),
173 | { body: devCommandArray }
174 | );
175 | log(`Successfully reloaded developer guild (/) commands for server: ${serverId}`, 'SUCCESS');
176 | } catch (error) {
177 | log(`Failed to reload developer guild (/) commands for server: ${serverId}`, 'ERROR');
178 | console.error(error);
179 | logErrorToFile(error)
180 | }
181 | });
182 |
183 | await Promise.all(promises);
184 | } else {
185 | log('No developer guild server IDs provided, or no developer commands to register.', 'WARNING');
186 | }
187 | };
188 |
189 | const handleCommands = async (client, commandsPath) => {
190 | const placeholderTokens = [
191 | "YOUR_BOT_TOKEN",
192 | "YOUR_MONGODB_URL",
193 | "YOUR_BOT_ID",
194 | "YOUR_DEVELOPER_GUILD_ID",
195 | "YOUR_BOT_OWNER_ID",
196 | "YOUR_DEVELOPER_COMMANDS_SERVER_ID_1",
197 | "YOUR_DEVELOPER_COMMANDS_SERVER_ID_2",
198 | "YOUR_GUILD_JOIN_LOGS_CHANNEL_ID",
199 | "YOUR_GUILD_LEAVE_LOGS_CHANNEL_ID",
200 | "YOUR_COMMAND_LOGS_CHANNEL_ID"
201 | ];
202 |
203 | if (isConfigIncomplete('botid', config.bot.id, placeholderTokens) || isConfigIncomplete('bottoken', config.bot.token, placeholderTokens)) {
204 | log("Missing or incorrect critical configuration.", 'ERROR');
205 | if (isConfigIncomplete('botid', config.bot.id, placeholderTokens)) {
206 | log("Bot ID is missing or incorrect. Please replace 'YOUR_BOT_ID' with your actual bot ID in config.json.", 'ERROR');
207 | }
208 | if (isConfigIncomplete('bottoken', config.bot.token, placeholderTokens)) {
209 | log("Bot token is missing or incorrect. Please replace 'YOUR_BOT_TOKEN' with your actual bot token in config.json.", 'ERROR');
210 | }
211 | process.exit(1);
212 | }
213 |
214 | if (!client.commands) {
215 | client.commands = new Collection();
216 | }
217 |
218 | const rest = new REST({ version: '10' }).setToken(config.bot.token);
219 | const { globalCommandArray, devCommandArray } = loadCommands(client, commandsPath);
220 | await registerCommands(globalCommandArray, devCommandArray, rest, config);
221 | const watcher = chokidar.watch([commandsPath, './src/functions', './src/schemas'], {
222 | persistent: true,
223 | ignoreInitial: true,
224 | awaitWriteFinish: true,
225 | });
226 |
227 | let timeout;
228 |
229 | const registerDebouncedCommands = async () => {
230 | const { globalCommandArray, devCommandArray } = loadCommands(client, commandsPath);
231 | await registerCommands(globalCommandArray, devCommandArray, rest, config);
232 | };
233 |
234 | watcher
235 | .on('add', (filePath) => {
236 | if (filePath.includes('schemas')) {
237 | log(`Schema file added: ${formatFilePath(filePath)}`, 'WARNING');
238 | return;
239 | }
240 |
241 | if (filePath.includes('functions')) {
242 | log(`Functions file added: ${formatFilePath(filePath)}`, 'WARNING');
243 | return;
244 | }
245 |
246 | log(`New command file added: ${formatFilePath(filePath)}`, 'SUCCESS');
247 | loadCommand(client, filePath);
248 | addActivity('added', filePath);
249 | clearTimeout(timeout);
250 | timeout = setTimeout(registerDebouncedCommands, 5000);
251 | })
252 | .on('change', (filePath) => {
253 | if (filePath.includes('schemas')) {
254 | log(`Schema file changed: ${formatFilePath(filePath)}`, 'WARNING');
255 | return;
256 | }
257 | if (filePath.includes('functions')) {
258 | log(`Functions file changed: ${formatFilePath(filePath)}`, 'WARNING')
259 | return;
260 | }
261 |
262 | log(`Command file changed: ${formatFilePath(filePath)}`, 'INFO');
263 | loadCommand(client, filePath);
264 | addActivity('changed', filePath);
265 | clearTimeout(timeout);
266 | timeout = setTimeout(registerDebouncedCommands, 5000);
267 | })
268 | .on('unlink', async (filePath) => {
269 | if (filePath.includes('schemas')) {
270 | log(`Schema file removed: ${formatFilePath(filePath)}`, 'WARNING');
271 | return;
272 | }
273 |
274 | if (filePath.includes('functions')) {
275 | log(`Functions file removed: ${formatFilePath(filePath)}`, 'WARNING');
276 | return;
277 | }
278 |
279 | const commandName = path.basename(filePath, '.js');
280 | log(`Command file removed: ${formatFilePath(filePath)}`, 'ERROR');
281 | client.commands.delete(commandName);
282 | await unregisterCommand(commandName, rest, config, devCommandArray);
283 | addActivity('removed', filePath);
284 | clearTimeout(timeout);
285 | timeout = setTimeout(registerDebouncedCommands, 5000);
286 | });
287 |
288 | };
289 |
290 | module.exports = {
291 | handleCommands,
292 | getActivities
293 | };
294 |
--------------------------------------------------------------------------------
/src/functions/handlers/prefixHandler.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | const { Collection } = require('discord.js');
4 | const fs = require('fs');
5 | const path = require('path');
6 | const chokidar = require('chokidar');
7 | const chalk = require('chalk');
8 |
9 | const debounce = (func, delay) => {
10 | let timeoutId;
11 | return (...args) => {
12 | clearTimeout(timeoutId);
13 | timeoutId = setTimeout(() => {
14 | func.apply(this, args);
15 | }, delay);
16 | };
17 | };
18 |
19 | const errorsDir = path.join(__dirname, '../../../errors');
20 |
21 | function ensureErrorDirectoryExists() {
22 | if (!fs.existsSync(errorsDir)) {
23 | fs.mkdirSync(errorsDir);
24 | }
25 | }
26 |
27 | function logErrorToFile(error) {
28 | ensureErrorDirectoryExists();
29 |
30 | // Convert the error object into a string, including the stack trace
31 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
32 |
33 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
34 | const filePath = path.join(errorsDir, fileName);
35 |
36 | fs.writeFileSync(filePath, errorMessage, 'utf8');
37 | }
38 |
39 |
40 | function prefixHandler(client, prefixPath) {
41 | client.prefix = new Collection();
42 |
43 | const log = (message, type = 'INFO') => {
44 | const colors = {
45 | INFO: chalk.blue.bold('INFO:'),
46 | SUCCESS: chalk.green.bold('SUCCESS:'),
47 | ERROR: chalk.red.bold('ERROR:'),
48 | WARNING: chalk.yellow.bold('WARNING:')
49 | };
50 | console.log(colors[type] + ' ' + message);
51 | };
52 |
53 | const loadCommand = (filePath) => {
54 | try {
55 | delete require.cache[require.resolve(filePath)];
56 | const command = require(filePath);
57 |
58 | if (command.name) {
59 | client.prefix.set(command.name, command);
60 | log(`Loaded Prefix command: ${chalk.green(command.name)}`, 'SUCCESS');
61 | } else {
62 | log(`Command in ${chalk.yellow(path.basename(filePath))} is missing a name.`, 'WARNING');
63 | }
64 | } catch (error) {
65 | log(`Failed to load prefix command in ${chalk.red(path.basename(filePath))}`, 'ERROR');
66 | console.error(error);
67 | logErrorToFile(error)
68 | }
69 | };
70 |
71 | const unloadCommand = (filePath) => {
72 | const commandName = path.basename(filePath, '.js');
73 | if (client.prefix.has(commandName)) {
74 | client.prefix.delete(commandName);
75 | log(`Unloaded command: ${chalk.red(commandName)}`, 'SUCCESS');
76 | } else {
77 | log(`Command "${chalk.yellow(commandName)}" not found in client collection.`, 'WARNING');
78 | }
79 | };
80 |
81 | const loadAllCommands = (commandDir) => {
82 | const commandFiles = fs.readdirSync(commandDir);
83 | commandFiles.forEach(file => {
84 | const filePath = path.join(commandDir, file);
85 | const stat = fs.statSync(filePath);
86 |
87 | if (stat.isDirectory()) {
88 | loadAllCommands(filePath);
89 | } else if (file.endsWith('.js')) {
90 | loadCommand(filePath);
91 | }
92 | });
93 | };
94 |
95 | loadAllCommands(prefixPath);
96 |
97 | const watcher = chokidar.watch(prefixPath, {
98 | persistent: true,
99 | ignoreInitial: true,
100 | awaitWriteFinish: true,
101 | });
102 |
103 | const debouncedLoadCommand = debounce(loadCommand, 500);
104 | const debouncedUnloadCommand = debounce(unloadCommand, 500);
105 |
106 | watcher
107 | .on('add', (filePath) => {
108 | if (filePath.endsWith('.js')) {
109 | log(`New command file added: ${chalk.green(path.basename(filePath))}`, 'SUCCESS');
110 | debouncedLoadCommand(filePath);
111 | }
112 | })
113 | .on('change', (filePath) => {
114 | if (filePath.endsWith('.js')) {
115 | log(`Command file changed: ${chalk.blue(path.basename(filePath))}`, 'INFO');
116 | debouncedUnloadCommand(filePath);
117 | debouncedLoadCommand(filePath);
118 | }
119 | })
120 | .on('unlink', (filePath) => {
121 | if (filePath.endsWith('.js')) {
122 | log(`Command file removed: ${chalk.red(path.basename(filePath))}`, 'ERROR');
123 | debouncedUnloadCommand(filePath);
124 | }
125 | });
126 | }
127 |
128 | module.exports = { prefixHandler };
129 |
130 |
--------------------------------------------------------------------------------
/src/functions/handlers/requiredIntents.js:
--------------------------------------------------------------------------------
1 | const { GatewayIntentBits } = require('discord.js');
2 |
3 | const REQUIRED_INTENTS = {
4 | 'guildCreate': GatewayIntentBits.Guilds,
5 | 'guildUpdate': GatewayIntentBits.Guilds,
6 | 'guildDelete': GatewayIntentBits.Guilds,
7 | 'channelCreate': GatewayIntentBits.Guilds,
8 | 'channelUpdate': GatewayIntentBits.Guilds,
9 | 'channelDelete': GatewayIntentBits.Guilds,
10 | 'channelPinsUpdate': GatewayIntentBits.Guilds,
11 | 'threadCreate': GatewayIntentBits.Guilds,
12 | 'threadUpdate': GatewayIntentBits.Guilds,
13 | 'threadDelete': GatewayIntentBits.Guilds,
14 | 'threadListSync': GatewayIntentBits.Guilds,
15 | 'threadMemberUpdate': GatewayIntentBits.Guilds,
16 | 'threadMembersUpdate': GatewayIntentBits.Guilds,
17 | 'stageInstanceCreate': GatewayIntentBits.Guilds,
18 | 'stageInstanceUpdate': GatewayIntentBits.Guilds,
19 | 'stageInstanceDelete': GatewayIntentBits.Guilds,
20 | 'guildMemberAdd': GatewayIntentBits.GuildMembers,
21 | 'guildMemberUpdate': GatewayIntentBits.GuildMembers,
22 | 'guildMemberRemove': GatewayIntentBits.GuildMembers,
23 | 'threadMembersUpdate': GatewayIntentBits.GuildMembers,
24 | 'guildAuditLogEntryCreate': GatewayIntentBits.GuildModeration,
25 | 'guildBanAdd': GatewayIntentBits.GuildModeration,
26 | 'guildBanRemove': GatewayIntentBits.GuildModeration,
27 | 'guildEmojisUpdate': GatewayIntentBits.GuildEmojisAndStickers,
28 | 'guildStickersUpdate': GatewayIntentBits.GuildEmojisAndStickers,
29 | 'guildIntegrationsUpdate': GatewayIntentBits.GuildIntegrations,
30 | 'integrationCreate': GatewayIntentBits.GuildIntegrations,
31 | 'integrationUpdate': GatewayIntentBits.GuildIntegrations,
32 | 'integrationDelete': GatewayIntentBits.GuildIntegrations,
33 | 'webhooksUpdate': GatewayIntentBits.GuildWebhooks,
34 | 'inviteCreate': GatewayIntentBits.GuildInvites,
35 | 'inviteDelete': GatewayIntentBits.GuildInvites,
36 | 'voiceStateUpdate': GatewayIntentBits.GuildVoiceStates,
37 | 'presenceUpdate': GatewayIntentBits.GuildPresences,
38 | 'messageCreate': GatewayIntentBits.GuildMessages | GatewayIntentBits.DirectMessages | GatewayIntentBits.MessageContent,
39 | 'messageUpdate': GatewayIntentBits.GuildMessages | GatewayIntentBits.DirectMessages,
40 | 'messageDelete': GatewayIntentBits.GuildMessages | GatewayIntentBits.DirectMessages,
41 | 'messageDeleteBulk': GatewayIntentBits.GuildMessages,
42 | 'messageReactionAdd': GatewayIntentBits.GuildMessageReactions,
43 | 'messageReactionRemove': GatewayIntentBits.GuildMessageReactions,
44 | 'messageReactionRemoveAll': GatewayIntentBits.GuildMessageReactions,
45 | 'messageReactionRemoveEmoji': GatewayIntentBits.GuildMessageReactions,
46 | 'typingStart': GatewayIntentBits.GuildMessageTyping,
47 | 'channelPinsUpdate': GatewayIntentBits.GuildMessages,
48 | 'guildScheduledEventCreate': GatewayIntentBits.GuildScheduledEvents,
49 | 'guildScheduledEventUpdate': GatewayIntentBits.GuildScheduledEvents,
50 | 'guildScheduledEventDelete': GatewayIntentBits.GuildScheduledEvents,
51 | 'guildScheduledEventUserAdd': GatewayIntentBits.GuildScheduledEvents,
52 | 'guildScheduledEventUserRemove': GatewayIntentBits.GuildScheduledEvents,
53 | 'autoModerationRuleCreate': GatewayIntentBits.AutoModerationConfiguration,
54 | 'autoModerationRuleUpdate': GatewayIntentBits.AutoModerationConfiguration,
55 | 'autoModerationRuleDelete': GatewayIntentBits.AutoModerationConfiguration,
56 | 'autoModerationActionExecution': GatewayIntentBits.AutoModerationExecution,
57 | };
58 |
59 | // Updated function
60 | function checkMissingIntents(client) {
61 | const missingIntents = new Set();
62 |
63 | const intents = Number(client.options.intents.bitfield);
64 |
65 | // Check missing intents based on required intents for each event
66 | for (const eventName of Object.keys(client._events)) {
67 | const required = REQUIRED_INTENTS[eventName];
68 | if (!required) continue;
69 | if ((intents & required) !== required) {
70 | missingIntents.add(required);
71 | }
72 | }
73 |
74 | if (missingIntents.size === 0) return;
75 |
76 | // Map intents to event names
77 | const EventNames = Object.fromEntries(
78 | Object.entries(REQUIRED_INTENTS).map(([eventName, intent]) => [intent, eventName])
79 | );
80 |
81 | const missingIntentNames = [...missingIntents].map(intent => {
82 | // Try to map intent value back to an event name, or display 'Unknown Intent' if not found
83 | return EventNames[intent] || `Unknown Intent (${intent})`;
84 | });
85 |
86 | console.warn('Warning: Missing intents detected:', missingIntentNames);
87 | }
88 |
89 | module.exports = { checkMissingIntents };
90 |
91 |
--------------------------------------------------------------------------------
/src/functions/handlers/similarity.js:
--------------------------------------------------------------------------------
1 | function getSimilarCommands(commandName, commands) {
2 | const similarityThreshold = 0.6;
3 | const partialMatches = [];
4 | const similarCommands = [];
5 |
6 | for (const command of commands) {
7 | const cmdName = command.name;
8 |
9 | if (cmdName.includes(commandName) || commandName.includes(cmdName)) {
10 | partialMatches.push(cmdName);
11 | }
12 |
13 | const similarity = calculateSimilarity(commandName, cmdName);
14 | if (similarity >= similarityThreshold) {
15 | similarCommands.push({ name: cmdName, similarity });
16 | }
17 |
18 | if (command.aliases && command.aliases.length > 0) {
19 | for (const alias of command.aliases) {
20 | if (alias.includes(commandName) || commandName.includes(alias)) {
21 | partialMatches.push(alias);
22 | }
23 |
24 | const aliasSimilarity = calculateSimilarity(commandName, alias);
25 | if (aliasSimilarity >= similarityThreshold) {
26 | similarCommands.push({ name: alias, similarity: aliasSimilarity });
27 | }
28 | }
29 | }
30 | }
31 |
32 | const combinedCommands = new Set([...partialMatches, ...similarCommands.map(cmd => cmd.name)]);
33 | const uniqueCommands = Array.from(combinedCommands);
34 |
35 | similarCommands.sort((a, b) => b.similarity - a.similarity);
36 |
37 | return uniqueCommands.sort((a, b) => {
38 | const aIndex = similarCommands.findIndex(cmd => cmd.name === a);
39 | const bIndex = similarCommands.findIndex(cmd => cmd.name === b);
40 | return bIndex - aIndex;
41 | });
42 | }
43 |
44 | function calculateSimilarity(str1, str2) {
45 | const len1 = str1.length;
46 | const len2 = str2.length;
47 | const matrix = [];
48 |
49 | for (let i = 0; i <= len1; i++) {
50 | matrix[i] = [i];
51 | }
52 | for (let j = 0; j <= len2; j++) {
53 | matrix[0][j] = j;
54 | }
55 |
56 | for (let i = 1; i <= len1; i++) {
57 | for (let j = 1; j <= len2; j++) {
58 | const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
59 | matrix[i][j] = Math.min(
60 | matrix[i - 1][j] + 1,
61 | matrix[i][j - 1] + 1,
62 | matrix[i - 1][j - 1] + cost
63 | );
64 | }
65 | }
66 |
67 | return 1 - matrix[len1][len2] / Math.max(len1, len2);
68 | }
69 |
70 | module.exports = { getSimilarCommands };
71 |
--------------------------------------------------------------------------------
/src/functions/handlers/watchFolders.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const chokidar = require('chokidar');
4 | const chalk = require('chalk');
5 |
6 | const commandsPath = path.join(__dirname, '../../commands');
7 | const eventsPath = path.join(__dirname, '../../events');
8 | const prefixPath = path.join(__dirname, '../../messages');
9 |
10 | const commandTemplate = `
11 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
12 |
13 | module.exports = {
14 | data: new SlashCommandBuilder()
15 | .setName('your-command')
16 | .setDescription('Describe your command here.'),
17 | async execute(interaction, client) {
18 | // Command execution logic goes here
19 | }
20 | };
21 | `;
22 |
23 | const prefixTemplate = `
24 | //! This is a basic structure for a prefix command in discoBase using discord.js
25 |
26 | module.exports = {
27 | name: 'command-name',
28 | description: 'command-description.',
29 | //* Optional: Aliases are alternative names for the command. Example: !p will also trigger the ping command.
30 | aliases: ['alaises_1', 'aliases_2'],
31 | // The run function is the main logic that gets executed when the command is called.
32 | run: async (client, message, args) => {
33 | // Command execution logic goes here
34 | },
35 | };
36 | `;
37 |
38 | const eventTemplate = `
39 | module.exports = {
40 | name: 'event-name',
41 | async execute(eventObject, client) {
42 | // Event handling logic goes here
43 | }
44 | };
45 | `;
46 |
47 | const commandWatcher = chokidar.watch(commandsPath, {
48 | ignored: /(^|[\/\\])\../,
49 | persistent: true,
50 | ignoreInitial: true,
51 | });
52 |
53 | const eventWatcher = chokidar.watch(eventsPath, {
54 | ignored: /(^|[\/\\])\../,
55 | persistent: true,
56 | ignoreInitial: true,
57 | });
58 |
59 | const prefixWatcher = chokidar.watch(prefixPath, {
60 | ignored: /(^|[\/\\])\../,
61 | persistent: true,
62 | ignoreInitial: true,
63 | });
64 |
65 | const logWithStyle = (message, type = 'info') => {
66 | switch (type) {
67 | case 'success':
68 | console.log(chalk.green.bold(`✔ ${message}`));
69 | break;
70 | case 'error':
71 | console.log(chalk.red.bold(`✖ ${message}`));
72 | break;
73 | case 'info':
74 | console.log(chalk.blueBright.bold(`ℹ ${message}`));
75 | break;
76 | case 'add':
77 | console.log(chalk.cyan.bold(`➕ ${message}`));
78 | break;
79 | default:
80 | console.log(message);
81 | }
82 | };
83 |
84 | const getRelativePath = (filePath) => {
85 | const srcPath = path.join(__dirname, '../../');
86 | return path.relative(srcPath, filePath);
87 | };
88 |
89 | commandWatcher.on('add', (filePath) => {
90 | const ext = path.extname(filePath);
91 |
92 | if (ext === '.js') {
93 | fs.readFile(filePath, 'utf8', (err, data) => {
94 | if (err) throw err;
95 |
96 | if (data.trim().length === 0) {
97 | fs.writeFile(filePath, commandTemplate.trim(), (err) => {
98 | if (err) throw err;
99 | logWithStyle(`Added basic command structure to ${getRelativePath(filePath)}`, 'add');
100 | });
101 | }
102 | });
103 | }
104 | });
105 |
106 | prefixWatcher.on('add', (filePath) => {
107 | const ext = path.extname(filePath);
108 |
109 | if (ext === '.js') {
110 | fs.readFile(filePath, 'utf8', (err, data) => {
111 | if (err) throw err;
112 |
113 | if (data.trim().length === 0) {
114 | fs.writeFile(filePath, prefixTemplate.trim(), (err) => {
115 | if (err) throw err;
116 | logWithStyle(`Added basic prefix command structure to ${getRelativePath(filePath)}`, 'add');
117 | });
118 | }
119 | });
120 | }
121 | });
122 |
123 | eventWatcher.on('add', (filePath) => {
124 | const ext = path.extname(filePath);
125 | const eventName = path.basename(filePath, ext);
126 |
127 | if (ext === '.js') {
128 | fs.readFile(filePath, 'utf8', (err, data) => {
129 | if (err) throw err;
130 |
131 | if (data.trim().length === 0) {
132 | fs.writeFile(filePath, eventTemplate.trim(), (err) => {
133 | if (err) throw err;
134 | logWithStyle(`Added basic event structure for ${eventName} to ${getRelativePath(filePath)}`, 'add');
135 | });
136 | }
137 | });
138 | }
139 | });
140 |
141 | commandWatcher.on('error', (error) => logWithStyle(`Command watcher error: ${error}`, 'error'));
142 | eventWatcher.on('error', (error) => logWithStyle(`Event watcher error: ${error}`, 'error'));
143 | prefixWatcher.on('error', (error) => logWithStyle(`Prefix watcher error: ${error}`, 'error'));
144 |
145 | logWithStyle('[Info] Watching for new files.', 'info');
146 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const { Client, GatewayIntentBits, Partials } = require(`discord.js`);
2 | const client = new Client({ intents: ['GuildMessages', 'MessageContent', 'DirectMessages', 'GuildMembers', 'Guilds'], }); //Guilds, GuildMembers : REQUIRED
3 | const chalk = require('chalk');
4 | const config = require('../config.json');
5 | const fs = require('fs');
6 | const { eventsHandler } = require('./functions/handlers/handelEvents');
7 | const path = require('path');
8 | const { checkMissingIntents } = require('./functions/handlers/requiredIntents');
9 | const { antiCrash } = require('./functions/handlers/antiCrash');
10 | antiCrash();
11 | require('./functions/handlers/watchFolders');
12 | const adminFolderPath = path.join(__dirname, '../admin');
13 | const dashboardFilePath = path.join(adminFolderPath, 'dashboard.js');
14 |
15 | const eventsPath = './events';
16 |
17 | const errorsDir = path.join(__dirname, '../../../errors');
18 |
19 | function ensureErrorDirectoryExists() {
20 | if (!fs.existsSync(errorsDir)) {
21 | fs.mkdirSync(errorsDir);
22 | }
23 | }
24 |
25 | function logErrorToFile(error) {
26 | ensureErrorDirectoryExists();
27 |
28 | // Convert the error object into a string, including the stack trace
29 | const errorMessage = `${error.name}: ${error.message}\n${error.stack}`;
30 |
31 | const fileName = `${new Date().toISOString().replace(/:/g, '-')}.txt`;
32 | const filePath = path.join(errorsDir, fileName);
33 |
34 | fs.writeFileSync(filePath, errorMessage, 'utf8');
35 | }
36 |
37 |
38 | (async () => {
39 | try {
40 | await client.login(config.bot.token);
41 | console.log(chalk.green.bold('SUCCESS: ') + 'Bot logged in successfully!');
42 | if (fs.existsSync(adminFolderPath) && fs.existsSync(dashboardFilePath)) {
43 | require(dashboardFilePath);
44 | console.log(chalk.green(chalk.green.bold('SUCCESS: Admin dashboard loaded successfully!.')));
45 |
46 | }
47 | require('./functions/handlers/functionHandler');
48 |
49 | await eventsHandler(client, path.join(__dirname, eventsPath));
50 | checkMissingIntents(client);
51 | } catch (error) {
52 | if (error.message === "An invalid token was provided.") {
53 | console.error(chalk.red.bold('ERROR: ') + 'The token provided for the Discord bot is invalid. Please check your configuration.');
54 | logErrorToFile(error)
55 | } else {
56 | console.error(chalk.red.bold('ERROR: ') + 'Failed to log in:', error);
57 | logErrorToFile(error)
58 | }
59 | }
60 | })();
61 |
62 | module.exports = client;
63 |
64 |
65 |
66 | //* You can start writing your custom bot logic from here. Add new features, commands, or events!
67 |
--------------------------------------------------------------------------------
/src/messages/Community/ping.js:
--------------------------------------------------------------------------------
1 | //! This is a basic structure for a prefix command in a discoBase using discord.js
2 |
3 | module.exports = {
4 | //* Required: Command name, used to trigger the command. Example: !ping
5 | name: "ping",
6 |
7 | //* Required: A brief description of what the command does, useful for help commands.
8 | description: "This is the ping command.",
9 |
10 | //* Optional: Aliases are alternative names for the command. Example: !p will also trigger the ping command.
11 | aliases: ['p'],
12 |
13 | //? Optional: Permissions that the bot requires to execute the command.
14 | //? botPermissions: ['SendMessages'], // Example: bot needs permission to send messages.
15 |
16 | //? Optional: Permissions that the user requires to use this command. Uncomment if needed.
17 | //? userPermissions: ['ManageMessages'], // Example: Only users with Manage Messages permission can use this command.
18 |
19 | //? Optional: Set this to true if only bot admins can use this command.
20 | //? adminOnly: true,
21 |
22 | //? Optional: Set this to true if only the bot owner can use this command.
23 | //? ownerOnly: true,
24 |
25 | //? Optional: Set this to true if only developers can use this command.
26 | //? devOnly: true, so if this true this slash command will only register for the server IDs you provided in config.json
27 |
28 | //? Optional: Cooldown period for the command in seconds to prevent spam.
29 | //? cooldown: 10,
30 |
31 | // The run function is the main logic that gets executed when the command is called.
32 | run: async (client, message, args) => {
33 | const ping = Date.now() - message.createdTimestamp;
34 |
35 | const latency = Math.abs(ping);
36 | const latencyFormatted = `${latency.toString().substring(0, 2)}ms`;
37 | const emoji = "⏱️";
38 |
39 | message.reply(`${emoji} Pong! Latency is ${latencyFormatted}!`);
40 | },
41 | };
42 |
--------------------------------------------------------------------------------