djs-builder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tsbuildinfo +1 -0
- package/README.md +243 -0
- package/dist/discord/builder/components/Buttons.d.ts +22 -0
- package/dist/discord/builder/components/Buttons.d.ts.map +1 -0
- package/dist/discord/builder/components/Buttons.js +80 -0
- package/dist/discord/builder/components/Buttons.js.map +1 -0
- package/dist/discord/builder/components/Menus.d.ts +31 -0
- package/dist/discord/builder/components/Menus.d.ts.map +1 -0
- package/dist/discord/builder/components/Menus.js +75 -0
- package/dist/discord/builder/components/Menus.js.map +1 -0
- package/dist/discord/builder/permissions/perms.d.ts +6 -0
- package/dist/discord/builder/permissions/perms.d.ts.map +1 -0
- package/dist/discord/builder/permissions/perms.js +68 -0
- package/dist/discord/builder/permissions/perms.js.map +1 -0
- package/dist/discord/builder/utils.d.ts +10 -0
- package/dist/discord/builder/utils.d.ts.map +1 -0
- package/dist/discord/builder/utils.js +13 -0
- package/dist/discord/builder/utils.js.map +1 -0
- package/dist/discord/events-handler/events.d.ts +3 -0
- package/dist/discord/events-handler/events.d.ts.map +1 -0
- package/dist/discord/events-handler/events.js +97 -0
- package/dist/discord/events-handler/events.js.map +1 -0
- package/dist/discord/events-handler/login.d.ts +4 -0
- package/dist/discord/events-handler/login.d.ts.map +1 -0
- package/dist/discord/events-handler/login.js +142 -0
- package/dist/discord/events-handler/login.js.map +1 -0
- package/dist/discord/events-handler/prefix-register.d.ts +7 -0
- package/dist/discord/events-handler/prefix-register.d.ts.map +1 -0
- package/dist/discord/events-handler/prefix-register.js +84 -0
- package/dist/discord/events-handler/prefix-register.js.map +1 -0
- package/dist/discord/events-handler/prefix-responder.d.ts +4 -0
- package/dist/discord/events-handler/prefix-responder.d.ts.map +1 -0
- package/dist/discord/events-handler/prefix-responder.js +89 -0
- package/dist/discord/events-handler/prefix-responder.js.map +1 -0
- package/dist/discord/events-handler/slash-register.d.ts +4 -0
- package/dist/discord/events-handler/slash-register.d.ts.map +1 -0
- package/dist/discord/events-handler/slash-register.js +84 -0
- package/dist/discord/events-handler/slash-register.js.map +1 -0
- package/dist/discord/events-handler/slash-responder.d.ts +4 -0
- package/dist/discord/events-handler/slash-responder.d.ts.map +1 -0
- package/dist/discord/events-handler/slash-responder.js +78 -0
- package/dist/discord/events-handler/slash-responder.js.map +1 -0
- package/dist/discord/events-handler/starter.d.ts +6 -0
- package/dist/discord/events-handler/starter.d.ts.map +1 -0
- package/dist/discord/events-handler/starter.js +57 -0
- package/dist/discord/events-handler/starter.js.map +1 -0
- package/dist/discord/functions/anticrash.d.ts +2 -0
- package/dist/discord/functions/anticrash.d.ts.map +1 -0
- package/dist/discord/functions/anticrash.js +50 -0
- package/dist/discord/functions/anticrash.js.map +1 -0
- package/dist/discord/functions/devLogs.d.ts +2 -0
- package/dist/discord/functions/devLogs.d.ts.map +1 -0
- package/dist/discord/functions/devLogs.js +127 -0
- package/dist/discord/functions/devLogs.js.map +1 -0
- package/dist/discord/functions/mongoDb.d.ts +4 -0
- package/dist/discord/functions/mongoDb.d.ts.map +1 -0
- package/dist/discord/functions/mongoDb.js +40 -0
- package/dist/discord/functions/mongoDb.js.map +1 -0
- package/dist/discord/functions/similarity.d.ts +4 -0
- package/dist/discord/functions/similarity.d.ts.map +1 -0
- package/dist/discord/functions/similarity.js +36 -0
- package/dist/discord/functions/similarity.js.map +1 -0
- package/dist/discord/functions/terminal.d.ts +3 -0
- package/dist/discord/functions/terminal.d.ts.map +1 -0
- package/dist/discord/functions/terminal.js +80 -0
- package/dist/discord/functions/terminal.js.map +1 -0
- package/dist/discord/functions/utils.d.ts +5 -0
- package/dist/discord/functions/utils.d.ts.map +1 -0
- package/dist/discord/functions/utils.js +11 -0
- package/dist/discord/functions/utils.js.map +1 -0
- package/dist/discord/functions/versedb.d.ts +3 -0
- package/dist/discord/functions/versedb.d.ts.map +1 -0
- package/dist/discord/functions/versedb.js +22 -0
- package/dist/discord/functions/versedb.js.map +1 -0
- package/dist/discord/types/starter.d.ts +97 -0
- package/dist/discord/types/starter.d.ts.map +1 -0
- package/dist/discord/types/starter.js +3 -0
- package/dist/discord/types/starter.js.map +1 -0
- package/dist/discord/types/utils.d.ts +3 -0
- package/dist/discord/types/utils.d.ts.map +1 -0
- package/dist/discord/types/utils.js +3 -0
- package/dist/discord/types/utils.js.map +1 -0
- package/dist/discord/utils.d.ts +4 -0
- package/dist/discord/utils.d.ts.map +1 -0
- package/dist/discord/utils.js +8 -0
- package/dist/discord/utils.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/lib/discord/builder/components/Buttons.ts +111 -0
- package/lib/discord/builder/components/Menus.ts +105 -0
- package/lib/discord/builder/permissions/perms.ts +66 -0
- package/lib/discord/builder/utils.ts +11 -0
- package/lib/discord/events-handler/events.ts +75 -0
- package/lib/discord/events-handler/login.ts +117 -0
- package/lib/discord/events-handler/prefix-register.ts +62 -0
- package/lib/discord/events-handler/prefix-responder.ts +105 -0
- package/lib/discord/events-handler/slash-register.ts +77 -0
- package/lib/discord/events-handler/slash-responder.ts +89 -0
- package/lib/discord/events-handler/starter.ts +56 -0
- package/lib/discord/functions/anticrash.ts +51 -0
- package/lib/discord/functions/devLogs.ts +116 -0
- package/lib/discord/functions/mongoDb.ts +38 -0
- package/lib/discord/functions/similarity.ts +44 -0
- package/lib/discord/functions/terminal.ts +95 -0
- package/lib/discord/functions/utils.ts +4 -0
- package/lib/discord/functions/versedb.ts +17 -0
- package/lib/discord/types/starter.ts +108 -0
- package/lib/discord/types/utils.ts +2 -0
- package/lib/discord/utils.ts +4 -0
- package/lib/index.ts +35 -0
- package/package.json +43 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ButtonManager } from './components/Buttons';
|
|
2
|
+
import { MenuManager } from './components/Menus';
|
|
3
|
+
import { PermissionChecker } from './permissions/perms';
|
|
4
|
+
|
|
5
|
+
const Components = {
|
|
6
|
+
ButtonManager,
|
|
7
|
+
MenuManager,
|
|
8
|
+
PermissionChecker
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { Components }
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Client } from 'discord.js';
|
|
2
|
+
import { readdir, stat } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { EventsOptions } from '../types/utils';
|
|
5
|
+
|
|
6
|
+
interface Event {
|
|
7
|
+
name: string;
|
|
8
|
+
once: boolean;
|
|
9
|
+
execute(...args: any[]): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function processEventFile(client: Client, filePath: string, eventsOptions: EventsOptions) {
|
|
13
|
+
try {
|
|
14
|
+
const stats = await stat(filePath);
|
|
15
|
+
if (stats.isDirectory() && eventsOptions.recursive) {
|
|
16
|
+
const filesInDirectory = await readdir(filePath);
|
|
17
|
+
if (filesInDirectory.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const item of filesInDirectory) {
|
|
21
|
+
const itemPath = join(filePath, item);
|
|
22
|
+
await processEventFile(client, itemPath, eventsOptions);
|
|
23
|
+
}
|
|
24
|
+
} else if (stats.isFile() && filePath.endsWith('.js')) {
|
|
25
|
+
const event: Event = await import(filePath);
|
|
26
|
+
if (eventsOptions.eventBlacklist && eventsOptions.eventBlacklist.includes(event.name)) {
|
|
27
|
+
console.log(`Event '${event.name}' is blacklisted and won't be loaded.`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (event.once) {
|
|
31
|
+
client.once(event.name, (...args: any[]) => event.execute(...args, client));
|
|
32
|
+
} else {
|
|
33
|
+
client.on(event.name, (...args: any[]) => event.execute(...args, client));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error(`Error in event file: ${filePath}`);
|
|
38
|
+
console.error(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function countEventFiles(filePath: string, eventsOptions: EventsOptions): Promise<number> {
|
|
43
|
+
let count = 0;
|
|
44
|
+
try {
|
|
45
|
+
const stats = await stat(filePath);
|
|
46
|
+
if (stats.isDirectory() && eventsOptions.recursive) {
|
|
47
|
+
const filesInDirectory = await readdir(filePath);
|
|
48
|
+
if (filesInDirectory.length === 0) {
|
|
49
|
+
return count;
|
|
50
|
+
}
|
|
51
|
+
for (const item of filesInDirectory) {
|
|
52
|
+
const itemPath = join(filePath, item);
|
|
53
|
+
count += await countEventFiles(itemPath, eventsOptions);
|
|
54
|
+
}
|
|
55
|
+
} else if (stats.isFile() && filePath.endsWith('.js')) {
|
|
56
|
+
count++;
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(`Error counting event files in directory: ${filePath}`);
|
|
60
|
+
console.error(error);
|
|
61
|
+
}
|
|
62
|
+
return count;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function loadEvents(client: any, eventsOptions: EventsOptions) {
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const eventSize = await countEventFiles(eventsOptions.path, eventsOptions);
|
|
69
|
+
client.eventSize = eventSize;
|
|
70
|
+
await processEventFile(client, eventsOptions.path, eventsOptions);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(`Error while loading events from path: ${eventsOptions.path}`);
|
|
73
|
+
console.error(error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Client, Collection, Routes, DataResolver, ActivityType } from 'discord.js';
|
|
2
|
+
import { BotOptions } from '../types/starter';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
|
|
5
|
+
export async function login(djs: Client, bot: BotOptions) {
|
|
6
|
+
|
|
7
|
+
const botData = new Collection<string, string | string[]>();
|
|
8
|
+
|
|
9
|
+
if (!djs) {
|
|
10
|
+
throw new Error("\x1b[31mMissing client parameter. Please provide a valid Discord client.\x1b[0m");
|
|
11
|
+
}
|
|
12
|
+
if (bot.token) {
|
|
13
|
+
try {
|
|
14
|
+
await djs.login(bot.token);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error("\x1b[31mInvalid token provided. Please provide a valid bot token\x1b[0m");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
djs.on('ready', async () => {
|
|
21
|
+
if (bot.name) djs.user?.setUsername(bot.name);
|
|
22
|
+
if (bot.avatar) {
|
|
23
|
+
const avatarPath = bot.avatar.startsWith('http') ? bot.avatar : path.join(process.cwd(), bot.avatar);
|
|
24
|
+
await djs.user?.setAvatar(avatarPath);
|
|
25
|
+
}
|
|
26
|
+
if (bot.banner) {
|
|
27
|
+
await djs.rest.patch(Routes.user(), {
|
|
28
|
+
body: { banner: await DataResolver.resolveImage(bot.banner) },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (bot.BotInfo?.botInvite) {
|
|
33
|
+
botData.set('inviteURL', bot.BotInfo?.botInvite);
|
|
34
|
+
}
|
|
35
|
+
if (bot.BotInfo && bot.BotInfo.perms) {
|
|
36
|
+
botData.set('permissions', bot.BotInfo.perms);
|
|
37
|
+
}
|
|
38
|
+
if (bot.BotInfo && bot.BotInfo.partners) {
|
|
39
|
+
botData.set('partners', bot.BotInfo.partners);
|
|
40
|
+
}
|
|
41
|
+
if (bot.BotInfo && bot.BotInfo.ownerId) {
|
|
42
|
+
botData.set('ownerId', bot.BotInfo.ownerId);
|
|
43
|
+
}
|
|
44
|
+
if (bot.BotInfo && bot.BotInfo.serverId) {
|
|
45
|
+
botData.set('serverId', bot.BotInfo.serverId);
|
|
46
|
+
}
|
|
47
|
+
if (bot.BotInfo && bot.BotInfo.serverInvite) {
|
|
48
|
+
botData.set('serverInvite', bot.BotInfo.serverInvite);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (bot.logs?.terminal) {
|
|
52
|
+
const { terminalLogs } = require('../functions/terminal');
|
|
53
|
+
terminalLogs(djs, bot);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (bot.logs?.devLogs?.enable) {
|
|
57
|
+
const { devLogger } = require('../functions/devLogs');
|
|
58
|
+
devLogger(bot.logs.devLogs);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const userChosenType = bot.Status?.type || 3;
|
|
62
|
+
if (bot.Status?.activities) {
|
|
63
|
+
if (bot.Status.activities.length >= 1) {
|
|
64
|
+
const initialActivity = bot.Status.activities[0];
|
|
65
|
+
if (bot.Status.state === 'idle' || bot.Status.state === 'online' || bot.Status.state === 'dnd' || bot.Status.state === 'invisible') {
|
|
66
|
+
djs.user?.setPresence({
|
|
67
|
+
activities: [{ name: initialActivity, type: userChosenType }],
|
|
68
|
+
status: bot.Status.state,
|
|
69
|
+
});
|
|
70
|
+
} else if (bot.Status.state === 'Streaming') {
|
|
71
|
+
djs.user?.setActivity({
|
|
72
|
+
name: initialActivity,
|
|
73
|
+
type: ActivityType.Streaming,
|
|
74
|
+
url: 'https://www.twitch.tv/discord'
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
throw new Error('Invalid bot Status.');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (bot.Status?.activities.length > 1) {
|
|
82
|
+
let delay = 60000;
|
|
83
|
+
if (bot.Status.delay !== undefined) {
|
|
84
|
+
if (bot.Status.delay < 60000) {
|
|
85
|
+
throw new Error('Delay must be at least 1 minute.');
|
|
86
|
+
}
|
|
87
|
+
delay = bot.Status.delay;
|
|
88
|
+
}
|
|
89
|
+
setInterval(() => {
|
|
90
|
+
try {
|
|
91
|
+
const randomActivity: string | undefined = bot.Status?.activities?.[Math.floor(Math.random() * bot.Status.activities?.length)];
|
|
92
|
+
|
|
93
|
+
if (randomActivity && bot.Status?.state === 'Streaming') {
|
|
94
|
+
djs.user?.setActivity({
|
|
95
|
+
name: randomActivity,
|
|
96
|
+
type: ActivityType.Streaming,
|
|
97
|
+
url: 'https://www.twitch.tv/discord'
|
|
98
|
+
});
|
|
99
|
+
} if (randomActivity && bot.Status?.state !== 'Streaming') {
|
|
100
|
+
djs.user?.setPresence({
|
|
101
|
+
activities: [{ name: randomActivity, type: userChosenType }],
|
|
102
|
+
status: bot.Status?.state,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(`Error setting activity: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
}, delay);
|
|
109
|
+
}
|
|
110
|
+
} else if (bot.Status?.activities !== undefined){
|
|
111
|
+
throw new Error('Activities array must be provided.');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
(djs as any).botData = botData;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Collection } from 'discord.js';
|
|
2
|
+
import { promises as fsPromises } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { Command } from '../types/utils';
|
|
5
|
+
|
|
6
|
+
export const commands = new Collection<string, Command>();
|
|
7
|
+
export const aliases = new Collection<string, string>();
|
|
8
|
+
export const commandNames = new Set<string>();
|
|
9
|
+
|
|
10
|
+
export async function readCommands(client: any, folderPath: string): Promise<number> {
|
|
11
|
+
let prefixCommandCount = 0;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
await fsPromises.access(folderPath);
|
|
15
|
+
const files = await fsPromises.readdir(folderPath, { withFileTypes: true });
|
|
16
|
+
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const filePath = join(folderPath, file.name);
|
|
19
|
+
|
|
20
|
+
if (file.isDirectory()) {
|
|
21
|
+
prefixCommandCount += await readCommands(client, filePath);
|
|
22
|
+
} else if (file.isFile()) {
|
|
23
|
+
try {
|
|
24
|
+
const command: Command = await import(filePath);
|
|
25
|
+
if (command && (command.execute || command.run)) {
|
|
26
|
+
commands.set(command.name, command);
|
|
27
|
+
commandNames.add(command.name);
|
|
28
|
+
|
|
29
|
+
if (command.aliases && Array.isArray(command.aliases)) {
|
|
30
|
+
command.aliases.forEach(alias => {
|
|
31
|
+
aliases.set(alias, command.name);
|
|
32
|
+
commandNames.add(alias);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
command.description = command.description || "No description provided.";
|
|
37
|
+
command.cooldown = command.cooldown || 1;
|
|
38
|
+
command.usage = command.usage || "No usage information provided.";
|
|
39
|
+
command.category = command.category || "Not categorized.";
|
|
40
|
+
command.owner = command.owner || false;
|
|
41
|
+
|
|
42
|
+
if (command.prefix) {
|
|
43
|
+
prefixCommandCount++;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
console.warn(`Ignoring invalid command file: ${filePath}`);
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(`Error in file: ${filePath}`);
|
|
50
|
+
console.error(error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
client.prefixSize = prefixCommandCount;
|
|
55
|
+
|
|
56
|
+
return prefixCommandCount;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error(`Error reading directory: ${folderPath}`);
|
|
59
|
+
console.error(error);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Client, Message, Collection, EmbedBuilder, TextChannel } from 'discord.js';
|
|
2
|
+
import { commands, aliases } from './prefix-register';
|
|
3
|
+
import { PrefixOptions } from '../types/utils';
|
|
4
|
+
import { getSimilarCommands } from '../functions/utils'
|
|
5
|
+
const botData = new Collection<string, string | string[]>();
|
|
6
|
+
const ownerId = botData.get('ownerId') as string;
|
|
7
|
+
const cooldowns: Collection<string, Collection<string, number>> = new Collection();
|
|
8
|
+
|
|
9
|
+
async function handleMessageCreate(message: Message, prefix: PrefixOptions): Promise<void> {
|
|
10
|
+
const botPrefix = prefix.prefix || '!';
|
|
11
|
+
if (!message.content.startsWith(botPrefix) || message.author.bot) return;
|
|
12
|
+
|
|
13
|
+
if (prefix.global === false && prefix.serverIds && prefix.serverIds.length > 0) {
|
|
14
|
+
|
|
15
|
+
const guildId = message.guild?.id;
|
|
16
|
+
if (guildId && !prefix.serverIds.includes(guildId)) return;
|
|
17
|
+
|
|
18
|
+
} else {
|
|
19
|
+
|
|
20
|
+
const args = message.content.slice(botPrefix.length).trim().split(/ +/);
|
|
21
|
+
const commandName = args.shift()?.toLowerCase();
|
|
22
|
+
|
|
23
|
+
if (!commandName || (!commands.has(commandName) && !aliases.has(commandName))) return;
|
|
24
|
+
|
|
25
|
+
const command = commands.get(commandName) || commands.get(aliases.get(commandName) || '');
|
|
26
|
+
|
|
27
|
+
if (!command) {
|
|
28
|
+
const similarCommands = getSimilarCommands(commandName, commands);
|
|
29
|
+
if (similarCommands.length > 0) {
|
|
30
|
+
await message.reply(`Command not found. Did you mean: ${similarCommands.join(', ')}?`);
|
|
31
|
+
} else {
|
|
32
|
+
await message.reply(`Command '${commandName}' doesn't exist.`);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (command.owner && message.author.id !== ownerId) {
|
|
38
|
+
await message.reply(`Only the bot owner can use this command.`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const timestamps = cooldowns.get(command.name) || new Collection<string, number>();
|
|
44
|
+
const cooldownAmount = (command.cooldown || 3) * 1000;
|
|
45
|
+
|
|
46
|
+
if (timestamps.has(message.author.id)) {
|
|
47
|
+
const expirationTime = timestamps.get(message.author.id) || 0;
|
|
48
|
+
const timeLeft = (expirationTime - now) / 1000;
|
|
49
|
+
|
|
50
|
+
if (now < expirationTime) {
|
|
51
|
+
await message.reply(`Please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${command.name}\` command.`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
timestamps.set(message.author.id, now);
|
|
57
|
+
setTimeout(() => timestamps.delete(message.author.id), cooldownAmount);
|
|
58
|
+
cooldowns.set(command.name, timestamps);
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
if (command.run) {
|
|
63
|
+
command.run(message.client, message, args);
|
|
64
|
+
} else if (command.execute) {
|
|
65
|
+
command.execute(message.client, message, args);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const channel = message.guild?.channels.cache.get(prefix.logsId || '') as TextChannel | undefined;
|
|
69
|
+
const userName = message.author.username;
|
|
70
|
+
const userId = message.author.id;
|
|
71
|
+
const serverName = message.guild?.name || null;
|
|
72
|
+
const serverId = message.guild?.id || null;
|
|
73
|
+
const messageLink = `https://discord.com/channels/${serverId}/${message.channel.id}/${message.id}`;
|
|
74
|
+
|
|
75
|
+
const embedLog = new EmbedBuilder()
|
|
76
|
+
.setColor("Blue")
|
|
77
|
+
.setThumbnail(message.client.user?.displayAvatarURL())
|
|
78
|
+
.setTitle("Use Prefix Command")
|
|
79
|
+
.setTimestamp()
|
|
80
|
+
.addFields(
|
|
81
|
+
{ name: "📧 Cmd:", value: `- ${command.name}`, inline: true },
|
|
82
|
+
{ name: "🤪 User:", value: `- ${userName} (<@${userId}>)`, inline: true },
|
|
83
|
+
{ name: "\u200B", value: "\u200B", inline: true },
|
|
84
|
+
{ name: "🏠 Server:", value: `- ${serverName}.\n- [\`${serverId}\`].`, inline: true },
|
|
85
|
+
{ name: "📩 Message:", value: `- [Link](${messageLink})`, inline: true },
|
|
86
|
+
{ name: "\u200B", value: "\u200B", inline: true },
|
|
87
|
+
{ name: "⏳ Date:", value: `- <t:${Math.floor(Date.now() / 1000)}:R>`, inline: true }
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (prefix.logsId && channel) {
|
|
91
|
+
await channel.send({ embeds: [embedLog] });
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error(`Error executing command ${command.name}:`, error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function loadPrefix(client: Client, prefix: PrefixOptions): Promise<void> {
|
|
100
|
+
if (!prefix.prefix) {
|
|
101
|
+
throw new Error("No command prefix provided. Please provide a command prefix.");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
client.on("messageCreate", async message => await handleMessageCreate(message, prefix));
|
|
105
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Collection, Guild } from 'discord.js';
|
|
2
|
+
import { REST } from '@discordjs/rest';
|
|
3
|
+
import { Routes } from 'discord-api-types/v10';
|
|
4
|
+
import { readdir } from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { SlashOptions } from '../types/starter';
|
|
7
|
+
|
|
8
|
+
export async function registerSlashCommands(client: any, token: string, slash: SlashOptions): Promise<Collection<string, any>> {
|
|
9
|
+
|
|
10
|
+
if (!token) {
|
|
11
|
+
throw new Error("Please provide valid bot token to register slash commands.");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const slashCommands = new Collection<string, any>();
|
|
15
|
+
const rest = new REST({ version: '10' }).setToken(token);
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const resolvedPath = path.join(slash.baseDir, slash.path);
|
|
19
|
+
const dirents = await readdir(resolvedPath, { withFileTypes: true });
|
|
20
|
+
|
|
21
|
+
for (const dirent of dirents) {
|
|
22
|
+
if (dirent.isDirectory()) {
|
|
23
|
+
const folderPath = path.join(resolvedPath, dirent.name);
|
|
24
|
+
const files = await readdir(folderPath);
|
|
25
|
+
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
try {
|
|
28
|
+
const command = require(path.join(folderPath, file));
|
|
29
|
+
if (command.data) {
|
|
30
|
+
if (command.cooldown && !isNaN(command.cooldown)) {
|
|
31
|
+
command.data.cooldown = command.cooldown || 0;
|
|
32
|
+
}
|
|
33
|
+
slashCommands.set(command.data.name, command);
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(`Error in file: ${path.join(folderPath, file)}`);
|
|
37
|
+
console.error(error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
try {
|
|
42
|
+
const command = require(path.join(resolvedPath, dirent.name));
|
|
43
|
+
if (command.data) {
|
|
44
|
+
if (command.cooldown && !isNaN(command.cooldown)) {
|
|
45
|
+
command.data.cooldown = command.cooldown || 0;
|
|
46
|
+
}
|
|
47
|
+
slashCommands.set(command.data.name, command);
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(`Error in file: ${path.join(resolvedPath, dirent.name)}`);
|
|
51
|
+
console.error(error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (slash.global && !slash.serverId) {
|
|
57
|
+
const slashCommandArray = Array.from(slashCommands.values()).map(command => command.data.toJSON());
|
|
58
|
+
await rest.put(Routes.applicationCommands((client.user?.id || '')), { body: slashCommandArray, headers: { Authorization: `Bot ${token}` } });
|
|
59
|
+
} else if (!slash.global && slash.serverId) {
|
|
60
|
+
const guild = client.guilds.cache.get(slash.serverId) as Guild | undefined;
|
|
61
|
+
if (guild) {
|
|
62
|
+
const slashCommandArray = Array.from(slashCommands.values()).map(command => command.data.toJSON());
|
|
63
|
+
await rest.put(Routes.applicationGuildCommands((client.user?.id || ''), guild.id), { body: slashCommandArray, headers: { Authorization: `Bot ${token}` } });
|
|
64
|
+
} else {
|
|
65
|
+
console.error(`Guild with ID ${slash.serverId} not found.`);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
const slashCommandArray = Array.from(slashCommands.values()).map(command => command.data.toJSON());
|
|
69
|
+
await rest.put(Routes.applicationCommands((client.user?.id || '')), { body: slashCommandArray, headers: { Authorization: `Bot ${token}` } });
|
|
70
|
+
}
|
|
71
|
+
} catch (error: any) {
|
|
72
|
+
console.error('Error registering slash commands:', error.message);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
client.slashSize = slashCommands.size;
|
|
76
|
+
return slashCommands;
|
|
77
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Client, Collection, Interaction, EmbedBuilder, Snowflake } from 'discord.js';
|
|
2
|
+
import { registerSlashCommands } from './slash-register';
|
|
3
|
+
import { SlashOptions } from '../types/starter';
|
|
4
|
+
|
|
5
|
+
export async function loadSlash(client: Client, token: string, options: SlashOptions): Promise<void> {
|
|
6
|
+
const slashCommands = await registerSlashCommands(client, token, options);
|
|
7
|
+
|
|
8
|
+
if (!slashCommands || slashCommands.size === 0) {
|
|
9
|
+
console.log('\x1b[33m%s\x1b[0m', 'No registered slash commands. SlachHandler won\'t work.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const interactionCooldowns: Collection<Snowflake, Collection<string, number>> = new Collection();
|
|
14
|
+
|
|
15
|
+
client.on('interactionCreate', async (interaction: Interaction) => {
|
|
16
|
+
if (!interaction.isCommand() || !interaction.guild || !interaction.isChatInputCommand() || interaction.user.bot) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const command = slashCommands.get(interaction.commandName);
|
|
21
|
+
if (!command) return;
|
|
22
|
+
|
|
23
|
+
if (command.cooldown && !isNaN(command.cooldown)) {
|
|
24
|
+
const userCooldowns = interactionCooldowns.get(interaction.user.id) || new Collection<string, number>();
|
|
25
|
+
const cooldownExpiration = userCooldowns.get(command.data.name);
|
|
26
|
+
if (cooldownExpiration && cooldownExpiration > Date.now()) {
|
|
27
|
+
const remainingCooldown = (cooldownExpiration - Date.now()) / 1000;
|
|
28
|
+
await interaction.reply(`Command is on cooldown. Please wait ${remainingCooldown.toFixed(1)} seconds.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
userCooldowns.set(command.data.name, Date.now() + command.cooldown * 1000);
|
|
32
|
+
interactionCooldowns.set(interaction.user.id, userCooldowns);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
if (command.run) {
|
|
38
|
+
await command.run(client, interaction);
|
|
39
|
+
} else if (command.execute) {
|
|
40
|
+
await command.execute(client, interaction);
|
|
41
|
+
} else {
|
|
42
|
+
console.log('\x1b[33m%s\x1b[0m', `Command "${command.data.name}" has neither run nor execute method.`);
|
|
43
|
+
}
|
|
44
|
+
const startExecutionTime = Date.now();
|
|
45
|
+
|
|
46
|
+
const channel = interaction.guild.channels.cache.get(options.logsId || '') as any;
|
|
47
|
+
const userName = interaction.user.username;
|
|
48
|
+
const userId = interaction.user.id;
|
|
49
|
+
const serverName = interaction.guild.name || 'Unknown Server';
|
|
50
|
+
const serverId = interaction.guild.id || 'Unknown ID';
|
|
51
|
+
let messageLink = '';
|
|
52
|
+
|
|
53
|
+
if (interaction.channel) {
|
|
54
|
+
messageLink = `https://discord.com/channels/${serverId}/${interaction.channel.id}/${interaction.id}`;
|
|
55
|
+
} else {
|
|
56
|
+
messageLink = 'Unknown';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
const embedLog = new EmbedBuilder()
|
|
61
|
+
.setColor("Blue")
|
|
62
|
+
.setThumbnail(interaction.client.user?.displayAvatarURL())
|
|
63
|
+
.setTitle("Use Prefix Command")
|
|
64
|
+
.setTimestamp()
|
|
65
|
+
.addFields(
|
|
66
|
+
{ name: "📧 Cmd:", value: `- ${command.name}`, inline: true },
|
|
67
|
+
{ name: "🤪 User:", value: `- ${userName} (<@${userId}>)`, inline: true },
|
|
68
|
+
{ name: "\u200B", value: "\u200B", inline: true },
|
|
69
|
+
{ name: "🏠 Server:", value: `- ${serverName}.\n- [\`${serverId}\`].`, inline: true },
|
|
70
|
+
{ name: "📩 Message:", value: `- [Link](${messageLink})`, inline: true },
|
|
71
|
+
{ name: "\u200B", value: "\u200B", inline: true },
|
|
72
|
+
{ name: "⏳ Date:", value: `- <t:${Math.floor(Date.now() / 1000)}:R>`, inline: true }
|
|
73
|
+
);
|
|
74
|
+
if (options.logsId && channel) {
|
|
75
|
+
await channel.send({ embeds: [embedLog] });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const executionTime = Date.now() - startExecutionTime;
|
|
79
|
+
if (executionTime > 3000) {
|
|
80
|
+
console.log('\x1b[33m%s\x1b[0m', `Command "${command.data.name}" took ${executionTime}ms to execute.`);
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('\x1b[31m%s\x1b[0m', `Error executing command "${command.data.name}":`, error);
|
|
84
|
+
if (interaction.channel) {
|
|
85
|
+
await interaction.channel.send({ content: 'An error occurred while executing the command.' });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { StarterOptions, StarterInterface } from '../types/utils';
|
|
2
|
+
import { loadEvents } from './events';
|
|
3
|
+
import { loadPrefix } from './prefix-responder';
|
|
4
|
+
import { loadSlash } from './slash-responder';
|
|
5
|
+
import { mongoConnect, getDb, verseConnect } from '../functions/utils';
|
|
6
|
+
import { login } from './login';
|
|
7
|
+
|
|
8
|
+
export class Starter implements StarterInterface {
|
|
9
|
+
async start(djs: any, options: StarterOptions): Promise<any> {
|
|
10
|
+
let mongoDb;
|
|
11
|
+
let verseDb;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
if (options.bot) {
|
|
15
|
+
await login(djs, options.bot);
|
|
16
|
+
}
|
|
17
|
+
if (options.events) {
|
|
18
|
+
await loadEvents(djs, options.events);
|
|
19
|
+
}
|
|
20
|
+
if (options.prefix) {
|
|
21
|
+
await loadPrefix(djs, options.prefix);
|
|
22
|
+
}
|
|
23
|
+
if (options.slash && options.bot.token) {
|
|
24
|
+
await loadSlash(djs, options.bot?.token, options.slash);
|
|
25
|
+
}
|
|
26
|
+
if (options.bot?.Database?.mongo) {
|
|
27
|
+
await mongoConnect(options.bot.Database.mongo.mongoURI, options.bot.Database.mongo.dbName || 'djshandler');
|
|
28
|
+
mongoDb = await getDb();
|
|
29
|
+
}
|
|
30
|
+
if (options.bot?.Database?.verse) {
|
|
31
|
+
verseDb = await verseConnect(options.bot.Database.verse);
|
|
32
|
+
}
|
|
33
|
+
if (options.anticrash?.enable) {
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
} catch (error: any) {
|
|
37
|
+
console.error('An error occurred during bot startup:', error.message);
|
|
38
|
+
await this.handleStartupError(error, djs, options);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
mongodb: mongoDb,
|
|
43
|
+
versedb: verseDb,
|
|
44
|
+
slashSize: djs.slashSize || 0,
|
|
45
|
+
prefixSize: djs.prefixSize || 0,
|
|
46
|
+
eventSize: djs.eventSize || 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async handleStartupError(error: any, djs: any, options: StarterOptions) {
|
|
51
|
+
console.error('Error during bot startup:', error);
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
this.start(djs, options);
|
|
54
|
+
}, 5000);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { WebhookClient, EmbedBuilder } from 'discord.js';
|
|
2
|
+
|
|
3
|
+
export async function AntiCrash(djs: any, options: any) {
|
|
4
|
+
try {
|
|
5
|
+
if (!options.AntiCrash.webhookURL) {
|
|
6
|
+
throw new Error('Please provide a valid webhook URL.');
|
|
7
|
+
}
|
|
8
|
+
const webhookClient = new WebhookClient({ url: options.AntiCrash.webhookURL });
|
|
9
|
+
|
|
10
|
+
process.on('uncaughtException', (error: any) => {
|
|
11
|
+
console.error('Uncaught Exception:');
|
|
12
|
+
console.error('Error:', error.message);
|
|
13
|
+
if (error.stack) {
|
|
14
|
+
console.error('Stack Trace:', error.stack);
|
|
15
|
+
const filenameMatch = error.stack.match(/\((.*?):\d+:\d+\)/);
|
|
16
|
+
const filename = filenameMatch ? filenameMatch[1] : 'Unknown File';
|
|
17
|
+
|
|
18
|
+
const embed = new EmbedBuilder()
|
|
19
|
+
.setTitle('Uncaught Exception')
|
|
20
|
+
.setDescription(`**File:** \`${filename}\`\n**Error:** \`${error.message}\`\n**Stack Trace:** \`\`\`${error.stack}\`\`\``)
|
|
21
|
+
.setColor('#FF0000')
|
|
22
|
+
.setTimestamp();
|
|
23
|
+
|
|
24
|
+
webhookClient.send({ content: options.AntiCrash?.mention || '', embeds: [embed] });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
process.on('unhandledRejection', (reason: any, promise: any) => {
|
|
29
|
+
console.error('Unhandled Rejection:');
|
|
30
|
+
console.error('Reason:', reason);
|
|
31
|
+
if (promise) {
|
|
32
|
+
console.error('Promise:', promise);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const stackTrace = reason?.stack || '';
|
|
36
|
+
const filenameMatch = stackTrace.match(/at\s+(.+):(\d+):(\d+)/);
|
|
37
|
+
const filename = filenameMatch ? filenameMatch[1] : 'Unknown File';
|
|
38
|
+
|
|
39
|
+
const embed = new EmbedBuilder()
|
|
40
|
+
.setTitle('Unhandled Rejection')
|
|
41
|
+
.setDescription(`**File:** \`${filename}\`\n**Reason:** \`${reason}\`\n**Promise:** \`${promise}\``)
|
|
42
|
+
.setColor('#FF0000')
|
|
43
|
+
.setTimestamp();
|
|
44
|
+
|
|
45
|
+
webhookClient.send({ content: options.AntiCrash?.mention || '', embeds: [embed] });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
console.error('An error occurred in AntiCrash function:', error.message);
|
|
50
|
+
}
|
|
51
|
+
}
|