@spatulox/simplediscordbot 1.0.24 → 1.0.26

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.
Files changed (37) hide show
  1. package/.env.example +2 -0
  2. package/dist/SimpleDiscordBotInfo.js +14 -0
  3. package/dist/bot/Bot.js +92 -0
  4. package/dist/bot/BotEnv.js +24 -0
  5. package/dist/bot/BotInteraction.js +74 -0
  6. package/dist/bot/BotLog.js +134 -0
  7. package/dist/bot/BotMessage.js +110 -0
  8. package/dist/manager/FileManager.js +136 -0
  9. package/dist/manager/builder/SendableComponentBuilder.js +62 -0
  10. package/dist/manager/direct/BasicUserManager.js +81 -0
  11. package/dist/manager/direct/UserManager.js +18 -0
  12. package/dist/manager/guild/ChannelManager/ForumChannelManager.js +33 -0
  13. package/dist/manager/guild/ChannelManager/GuildChannelList.js +20 -0
  14. package/dist/manager/guild/ChannelManager/GuildChannelManager.js +91 -0
  15. package/dist/manager/guild/ChannelManager/GuildMessageManager.js +93 -0
  16. package/dist/manager/guild/ChannelManager/GuildTextChannelManager.js +33 -0
  17. package/dist/manager/guild/ChannelManager/GuildVoiceChannelManager.js +33 -0
  18. package/dist/manager/guild/ChannelManager/NewsChannelManager.js +33 -0
  19. package/dist/manager/guild/ChannelManager/StageChannelManager.js +33 -0
  20. package/dist/manager/guild/ChannelManager/ThreadChannelManager.js +49 -0
  21. package/dist/manager/guild/GuildManager.js +144 -0
  22. package/dist/manager/guild/GuildUserManager.js +251 -0
  23. package/dist/manager/guild/InviteManager.js +68 -0
  24. package/dist/manager/guild/InviteManager_old.js +89 -0
  25. package/dist/manager/guild/RoleManager.js +83 -0
  26. package/dist/manager/interactions/ModalManager.js +113 -0
  27. package/dist/manager/interactions/SelectMenuManager.js +123 -0
  28. package/dist/manager/messages/EmbedManager.js +161 -0
  29. package/dist/manager/messages/ReactionManager.js +99 -0
  30. package/dist/manager/messages/WebhookManager.js +108 -0
  31. package/dist/type/FolderName.js +9 -0
  32. package/dist/utils/DiscordRegex.js +117 -0
  33. package/dist/utils/Log.js +28 -0
  34. package/dist/utils/SimpleMutex.js +39 -0
  35. package/dist/utils/network/InternetChecker.js +54 -0
  36. package/dist/utils/times/UnitTime.js +85 -0
  37. package/package.json +2 -4
package/.env.example ADDED
@@ -0,0 +1,2 @@
1
+ DISCORD_BOT_TOKEN=example
2
+ DISCORD_BOT_DEV # Define this one with anything you want, the value is not important as long as the env var is defined
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SimpleDiscordBotInfo = void 0;
7
+ const package_json_1 = __importDefault(require("../package.json"));
8
+ exports.SimpleDiscordBotInfo = {
9
+ name: package_json_1.default.name,
10
+ version: package_json_1.default.license,
11
+ author: package_json_1.default.author,
12
+ description: package_json_1.default.description,
13
+ license: package_json_1.default.license
14
+ };
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Bot = void 0;
4
+ const discord_js_1 = require("discord.js");
5
+ const UnitTime_1 = require("../utils/times/UnitTime");
6
+ const Log_1 = require("../utils/Log");
7
+ const InternetChecker_1 = require("../utils/network/InternetChecker");
8
+ const BotLog_1 = require("./BotLog");
9
+ const EmbedManager_1 = require("../manager/messages/EmbedManager");
10
+ const BotMessage_1 = require("./BotMessage");
11
+ const BotEnv_1 = require("./BotEnv");
12
+ const BotInteraction_1 = require("./BotInteraction");
13
+ class Bot {
14
+ get config() { return Bot._config; }
15
+ get client() { return Bot._client; }
16
+ static get client() { return Bot._client; }
17
+ static get config() { return Bot._config; }
18
+ constructor(client, config) {
19
+ Log_1.Log.info('----------------------------------------------------');
20
+ Log_1.Log.info("Starting Bot");
21
+ Bot.criticConfig = { dev: BotEnv_1.BotEnv.dev, token: BotEnv_1.BotEnv.token };
22
+ Bot._config = { ...config, clientId: BotEnv_1.BotEnv.clientId };
23
+ Bot._client = client;
24
+ (async () => {
25
+ Log_1.Log.info(`Using discord.js version: ${discord_js_1.version}`);
26
+ Log_1.Log.info('Trying to connect to Discord Servers');
27
+ await InternetChecker_1.InternetChecker.checkConnection(3);
28
+ await this.login();
29
+ })();
30
+ }
31
+ async login(maxTries = 3) {
32
+ let success = false;
33
+ let tries = 0;
34
+ while (!success && tries < maxTries) {
35
+ try {
36
+ await Bot._client.login(Bot.criticConfig.token);
37
+ success = true;
38
+ Bot._client.on(discord_js_1.Events.ClientReady, async () => {
39
+ if (Bot._client.user) {
40
+ await Bot.log.initDiscordLogging();
41
+ Log_1.Log.info(`Connected on ${Bot._client.guilds.cache.size} servers as ${Bot._client.user.tag}`);
42
+ //Bot._client.guilds.cache.forEach(g => console.log(` - ${g.name}`));
43
+ Bot.log.info(EmbedManager_1.EmbedManager.description("Bot Started"));
44
+ }
45
+ });
46
+ }
47
+ catch (error) {
48
+ Log_1.Log.error(`Connection error : ${error}. Trying again...`);
49
+ tries++;
50
+ await new Promise(resolve => setTimeout(resolve, UnitTime_1.Time.second.SEC_03.toMilliseconds()));
51
+ }
52
+ }
53
+ if (!success) {
54
+ Log_1.Log.error('Impossible to connect the bot after 3 attempts');
55
+ return false;
56
+ }
57
+ return true;
58
+ }
59
+ static setActivity(message, type) {
60
+ if (Bot._client.user) {
61
+ Bot._client.user.setActivity({ name: message, type });
62
+ Log_1.Log.info(`Activity defined : ${message}`);
63
+ }
64
+ }
65
+ static setRandomActivity(randomActivity, intervalMs = null) {
66
+ if (randomActivity.length == 0) {
67
+ Log_1.Log.error("Bot.randomActivity = [{}] is empty");
68
+ return;
69
+ }
70
+ const pickRandom = () => {
71
+ const random = randomActivity[Math.floor(Math.random() * randomActivity.length)];
72
+ Bot.setActivity(random.message, random.type);
73
+ return random;
74
+ };
75
+ if (intervalMs === null) {
76
+ pickRandom();
77
+ Log_1.Log.info(`Activity set ONCE`);
78
+ return;
79
+ }
80
+ pickRandom();
81
+ setInterval(async () => {
82
+ pickRandom();
83
+ }, intervalMs);
84
+ Log_1.Log.info(`Random activity started (every ${Math.round(intervalMs / 60000)}min)`);
85
+ return;
86
+ }
87
+ }
88
+ exports.Bot = Bot;
89
+ // Static ref
90
+ Bot.log = BotLog_1.BotLog;
91
+ Bot.message = BotMessage_1.BotMessage;
92
+ Bot.interaction = BotInteraction_1.BotInteraction;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BotEnv = void 0;
4
+ const DiscordRegex_1 = require("../utils/DiscordRegex");
5
+ exports.BotEnv = {
6
+ get token() {
7
+ const token = process.env.DISCORD_BOT_TOKEN;
8
+ if (!token)
9
+ throw new Error('Missing environment variable : DISCORD_BOT_TOKEN');
10
+ return token;
11
+ },
12
+ get dev() {
13
+ return process.env.DEV === 'true';
14
+ },
15
+ get clientId() {
16
+ const token = process.env.DISCORD_BOT_CLIENTID;
17
+ if (!token)
18
+ throw new Error('Missing environment variable : DISCORD_BOT_CLIENTID');
19
+ if (!DiscordRegex_1.DiscordRegex.BOT_ID.test(token)) {
20
+ throw new Error("Invalid token format");
21
+ }
22
+ return token;
23
+ }
24
+ };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BotInteraction = void 0;
4
+ const SendableComponentBuilder_1 = require("../manager/builder/SendableComponentBuilder");
5
+ class BotInteraction {
6
+ /**
7
+ * InteractionReplyOptions && InteractionUpdateOptions
8
+ * The two have "content", "embeds" & "flags" field, so an internal cast is ok, unless discord/discordjs deprecate it
9
+ */
10
+ static buildReplyOptions(content, component, ephemeral) {
11
+ return this._buildOptions(content, component, ephemeral);
12
+ }
13
+ static buildUpdateOptions(content, component) {
14
+ return this._buildOptions(content, component, false);
15
+ }
16
+ static _buildOptions(content, component, ephemeral) {
17
+ return SendableComponentBuilder_1.SendableComponentBuilder.buildInteraction(content, component, ephemeral);
18
+ }
19
+ static async send(interaction, content, component = false, ephemeral = false) {
20
+ if (!interaction.isRepliable())
21
+ return false;
22
+ const options = this.buildReplyOptions(typeof content === 'string' ? content : "", typeof content === 'string' ? component : content, typeof content === 'string' ? ephemeral : component);
23
+ if (!interaction.deferred && !interaction.replied) {
24
+ return await interaction.reply(options);
25
+ }
26
+ else {
27
+ return await interaction.followUp(options);
28
+ }
29
+ }
30
+ static async reply(interaction, content, component = false, ephemeral = false) {
31
+ if (!interaction.isRepliable())
32
+ return false;
33
+ const options = this.buildReplyOptions(typeof content === 'string' ? content : "", typeof content === 'string' ? component : content, typeof content === 'string' ? ephemeral : component);
34
+ return interaction.reply(options);
35
+ }
36
+ static async followUp(interaction, content, component = false, ephemeral = false) {
37
+ if (!interaction.isRepliable())
38
+ return false;
39
+ const options = this.buildReplyOptions(typeof content === 'string' ? content : "", typeof content === 'string' ? component : content, typeof content === 'string' ? ephemeral : component);
40
+ return interaction.followUp(options);
41
+ }
42
+ static async defer(interaction) {
43
+ if (!interaction.isRepliable())
44
+ return;
45
+ if (interaction.isChatInputCommand() || interaction.isContextMenuCommand()) {
46
+ if (!interaction.deferred && !interaction.replied) {
47
+ await interaction.deferReply();
48
+ }
49
+ return;
50
+ }
51
+ if (interaction.isButton() ||
52
+ interaction.isAnySelectMenu() ||
53
+ interaction.isModalSubmit()) {
54
+ if (!interaction.deferred && !interaction.replied) {
55
+ await interaction.deferUpdate();
56
+ }
57
+ }
58
+ }
59
+ static async update(interaction, content, component) {
60
+ if (!interaction.isRepliable())
61
+ return false;
62
+ const options = this.buildUpdateOptions(typeof content === 'string' ? content : "", typeof content === 'string' ? component : content);
63
+ // MessageComponent → update()
64
+ if (interaction.isMessageComponent()) {
65
+ return await interaction.update(options);
66
+ }
67
+ // Slash commands → editReply()
68
+ if (interaction.deferred || interaction.replied) {
69
+ return await interaction.editReply(options);
70
+ }
71
+ return false;
72
+ }
73
+ }
74
+ exports.BotInteraction = BotInteraction;
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BotLog = void 0;
4
+ // src/utils/BotLog.ts
5
+ const discord_js_1 = require("discord.js");
6
+ const Log_1 = require("../utils/Log");
7
+ const Bot_1 = require("./Bot");
8
+ class BotLog {
9
+ constructor() { }
10
+ static config() {
11
+ return Bot_1.Bot.config.log;
12
+ }
13
+ /**
14
+ * Initialize Discord logging channels and update Bot.log references
15
+ */
16
+ static async initDiscordLogging() {
17
+ if (!Bot_1.Bot.client.isReady()) {
18
+ Log_1.Log.warn('Client not ready for Discord logging init');
19
+ return;
20
+ }
21
+ if (Bot_1.Bot.config.log?.logChannelId) {
22
+ try {
23
+ const logCh = await Bot_1.Bot.client.channels.fetch(Bot_1.Bot.config.log.logChannelId);
24
+ if (logCh?.isTextBased()) {
25
+ BotLog.logChannel = logCh;
26
+ }
27
+ else {
28
+ Log_1.Log.warn(`❌ Log channel ${Bot_1.Bot.config.log.logChannelId} invalid`);
29
+ }
30
+ }
31
+ catch (error) {
32
+ Log_1.Log.error(`❌ Log channel fetch failed: ${error}`);
33
+ }
34
+ }
35
+ if (Bot_1.Bot.config.log?.errorChannelId) {
36
+ try {
37
+ const errorCh = await Bot_1.Bot.client.channels.fetch(Bot_1.Bot.config.log.errorChannelId);
38
+ if (errorCh?.isTextBased()) {
39
+ BotLog.errorChannel = errorCh;
40
+ }
41
+ else {
42
+ Log_1.Log.warn(`❌ Error channel ${Bot_1.Bot.config.log.errorChannelId} invalid`);
43
+ }
44
+ }
45
+ catch (error) {
46
+ Log_1.Log.error(`❌ Error channel fetch failed: ${error}`);
47
+ }
48
+ }
49
+ }
50
+ /**
51
+ * Send content to specific Discord channel
52
+ */
53
+ static async _sendToChannel(channel, content, prefix = 'info') {
54
+ if (!channel)
55
+ return;
56
+ let msg;
57
+ try {
58
+ if (content instanceof discord_js_1.EmbedBuilder) {
59
+ msg = await channel.send({ embeds: [content] });
60
+ }
61
+ else {
62
+ const timestamp = `\`${new Date().toISOString()}\``;
63
+ msg = await channel.send(`${prefix.toUpperCase()} ${timestamp} ${content}`);
64
+ }
65
+ }
66
+ catch (error) {
67
+ Log_1.Log.error(`Failed to send to Discord channel: ${error}`);
68
+ }
69
+ return msg;
70
+ }
71
+ /**
72
+ * Send INFO log - TEXT or EMBED ! Respecte config.log.info
73
+ */
74
+ static async info(content) {
75
+ const logConfig = Bot_1.Bot.config.log;
76
+ // 1. CONSOLE selon config (ou défaut ON)
77
+ if (!logConfig || logConfig.info.console) {
78
+ if (!(content instanceof discord_js_1.EmbedBuilder)) {
79
+ Log_1.Log.info(content);
80
+ }
81
+ }
82
+ // 2. Discord seulement si config + channel
83
+ if (logConfig?.info.discord && this.logChannel) {
84
+ return await this._sendToChannel(this.logChannel, content, 'info');
85
+ }
86
+ }
87
+ /**
88
+ * Send ERROR log - TEXT or EMBED ! Respecte config.log.error
89
+ */
90
+ static async error(content) {
91
+ const logConfig = Bot_1.Bot.config.log;
92
+ // 1. CONSOLE selon config (ou défaut ON)
93
+ if (!logConfig || logConfig.error.console) {
94
+ if (!(content instanceof discord_js_1.EmbedBuilder)) {
95
+ Log_1.Log.error(content);
96
+ }
97
+ }
98
+ // 2. Discord seulement si config + channel
99
+ if (logConfig?.error.discord && this.errorChannel) {
100
+ return await this._sendToChannel(this.errorChannel, content, 'error');
101
+ }
102
+ }
103
+ /**
104
+ * Send WARNING log - TEXT or EMBED ! Respecte config.log.warn
105
+ */
106
+ static async warn(content) {
107
+ const logConfig = Bot_1.Bot.config.log;
108
+ if (!logConfig || logConfig?.warn.console) {
109
+ if (!(content instanceof discord_js_1.EmbedBuilder)) {
110
+ Log_1.Log.warn(content);
111
+ }
112
+ }
113
+ if (logConfig?.warn.discord && this.logChannel) {
114
+ return await this._sendToChannel(this.logChannel, content, 'warn');
115
+ }
116
+ }
117
+ /**
118
+ * Send DEBUG log - TEXT or EMBED ! Respecte config.log.debug
119
+ */
120
+ static async debug(content) {
121
+ const logConfig = Bot_1.Bot.config.log;
122
+ if (!logConfig || logConfig?.debug.console) {
123
+ if (!(content instanceof discord_js_1.EmbedBuilder)) {
124
+ Log_1.Log.debug(content);
125
+ }
126
+ }
127
+ if (logConfig?.debug.discord && this.logChannel) {
128
+ return await this._sendToChannel(this.logChannel, content, 'debug');
129
+ }
130
+ }
131
+ }
132
+ exports.BotLog = BotLog;
133
+ BotLog.logChannel = null;
134
+ BotLog.errorChannel = null;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BotMessage = void 0;
4
+ const discord_js_1 = require("discord.js");
5
+ const Log_1 = require("../utils/Log");
6
+ const Bot_1 = require("./Bot");
7
+ const EmbedManager_1 = require("../manager/messages/EmbedManager");
8
+ const SendableComponentBuilder_1 = require("../manager/builder/SendableComponentBuilder");
9
+ class BotMessage {
10
+ static async send(channel, content, component) {
11
+ try {
12
+ if (!channel) {
13
+ Log_1.Log.warn('Cannot send message: invalid channel');
14
+ return null;
15
+ }
16
+ if (typeof channel === "string") {
17
+ const fetchedChannel = Bot_1.Bot.client.channels.cache.get(channel);
18
+ if (!fetchedChannel?.isTextBased()) {
19
+ Log_1.Log.warn(`Invalid channel ID: ${channel}`);
20
+ return null;
21
+ }
22
+ channel = fetchedChannel;
23
+ }
24
+ let messageCreate;
25
+ if (typeof content !== "string" && !component) {
26
+ if (SendableComponentBuilder_1.SendableComponentBuilder.isSendableComponent(content)) {
27
+ messageCreate = SendableComponentBuilder_1.SendableComponentBuilder.buildMessage(content);
28
+ }
29
+ else {
30
+ messageCreate = content;
31
+ }
32
+ }
33
+ else {
34
+ content = content;
35
+ if (content && component) {
36
+ messageCreate = SendableComponentBuilder_1.SendableComponentBuilder.buildMessage(content, component);
37
+ }
38
+ else if (content) {
39
+ messageCreate = SendableComponentBuilder_1.SendableComponentBuilder.buildMessage(content);
40
+ }
41
+ else if (component) {
42
+ messageCreate = SendableComponentBuilder_1.SendableComponentBuilder.buildMessage(component);
43
+ }
44
+ else {
45
+ throw new Error("content and component cannot be null at the same time");
46
+ }
47
+ }
48
+ try {
49
+ return await channel.send(messageCreate);
50
+ }
51
+ catch (e) {
52
+ throw e;
53
+ }
54
+ }
55
+ catch (e) {
56
+ Log_1.Log.error(`Failed to send message : ${e}`);
57
+ return null;
58
+ }
59
+ }
60
+ static async sendDM(user, content, component) {
61
+ try {
62
+ let targetUser;
63
+ if (user instanceof discord_js_1.User || user instanceof discord_js_1.GuildMember) {
64
+ targetUser = user instanceof discord_js_1.GuildMember ? user.user : user;
65
+ }
66
+ else {
67
+ targetUser = await Bot_1.Bot.client.users.fetch(user);
68
+ }
69
+ let messageCreate;
70
+ if (content && component) {
71
+ messageCreate = SendableComponentBuilder_1.SendableComponentBuilder.buildMessage(content, component);
72
+ }
73
+ else if (content) {
74
+ messageCreate = SendableComponentBuilder_1.SendableComponentBuilder.buildMessage(content);
75
+ }
76
+ else if (component) {
77
+ messageCreate = SendableComponentBuilder_1.SendableComponentBuilder.buildMessage(component);
78
+ }
79
+ else {
80
+ throw new Error("content and component cannot be null at the same time");
81
+ }
82
+ return await targetUser.send(messageCreate);
83
+ }
84
+ catch (error) {
85
+ Log_1.Log.error(`Failed to send message to ${user}: ${error}`);
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * Quick success message
91
+ */
92
+ static success(channel, message) {
93
+ const embed = EmbedManager_1.EmbedManager.success(message);
94
+ if (channel instanceof discord_js_1.User || channel instanceof discord_js_1.GuildMember) {
95
+ return this.sendDM(channel, message, embed);
96
+ }
97
+ return this.send(channel, message, embed);
98
+ }
99
+ /**
100
+ * Quick error message
101
+ */
102
+ static error(channel, message) {
103
+ const embed = EmbedManager_1.EmbedManager.error(message);
104
+ if (channel instanceof discord_js_1.User || channel instanceof discord_js_1.GuildMember) {
105
+ return this.sendDM(channel, message, embed);
106
+ }
107
+ return this.send(channel, message, embed);
108
+ }
109
+ }
110
+ exports.BotMessage = BotMessage;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileManager = void 0;
7
+ // src/filesystem/FileManager.ts
8
+ const path_1 = __importDefault(require("path"));
9
+ const promises_1 = __importDefault(require("fs/promises"));
10
+ const Log_1 = require("../utils/Log");
11
+ const Bot_1 = require("../bot/Bot");
12
+ const EmbedManager_1 = require("./messages/EmbedManager");
13
+ class FileManager {
14
+ /**
15
+ * Reads a JSON file synchronously.
16
+ * @param filePath Full path to the JSON file
17
+ * @returns Parsed JSON object or 'Error' string on failure
18
+ */
19
+ static async readJsonFile(filePath) {
20
+ try {
21
+ const data = await promises_1.default.readFile(filePath, 'utf8');
22
+ return JSON.parse(data);
23
+ }
24
+ catch (error) {
25
+ Log_1.Log.error(`Failed to read JSON file ${filePath}: ${error}`);
26
+ return false;
27
+ }
28
+ }
29
+ /**
30
+ * Lists all directories in a given path.
31
+ * @param directoryPath Path to scan for directories
32
+ * @returns Array of directory names or false on error
33
+ */
34
+ static async listDirectories(directoryPath) {
35
+ try {
36
+ const files = await promises_1.default.readdir(directoryPath, { withFileTypes: true });
37
+ const directories = files
38
+ .filter(file => file.isDirectory())
39
+ .map(dir => dir.name);
40
+ return directories;
41
+ }
42
+ catch (error) {
43
+ Log_1.Log.error(`Failed to read directory ${directoryPath}: ${error}`);
44
+ return false;
45
+ }
46
+ }
47
+ /**
48
+ * Lists all JSON files in a directory.
49
+ * @param directoryPath Path to scan for JSON files
50
+ * @returns Array of JSON filenames or false on error
51
+ */
52
+ static async listJsonFiles(directoryPath) {
53
+ try {
54
+ const files = await promises_1.default.readdir(directoryPath);
55
+ return files.filter(file => path_1.default.extname(file) === '.json');
56
+ }
57
+ catch (error) {
58
+ Log_1.Log.error(`Failed to read directory ${directoryPath}: ${error}`);
59
+ return false;
60
+ }
61
+ }
62
+ /**
63
+ * Lists files with specific extension in a directory.
64
+ * @param directoryPath Path to scan
65
+ * @param extension File extension (with or without dot)
66
+ * @returns Array of matching filenames or 'Error' string on failure
67
+ */
68
+ static async listFiles(directoryPath, extension) {
69
+ if (typeof directoryPath !== 'string' || typeof extension !== 'string') {
70
+ Log_1.Log.error('Directory path and extension must be strings');
71
+ return false;
72
+ }
73
+ try {
74
+ let ext = extension;
75
+ if (ext.startsWith('.')) {
76
+ ext = ext.slice(1);
77
+ }
78
+ const files = await promises_1.default.readdir(directoryPath);
79
+ return files.filter(file => path_1.default.extname(file) === `.${ext}`);
80
+ }
81
+ catch (error) {
82
+ Log_1.Log.error(`Failed to read directory ${directoryPath}: ${error}`);
83
+ return false;
84
+ }
85
+ }
86
+ /**
87
+ * Creates directory structure and writes JSON data to file.
88
+ * @param directoryPath Full directory path (creates if missing)
89
+ * @param filename Filename without extension
90
+ * @param data Data to write (JSON serializable)
91
+ * @param sendErrorToErrorChannel Send error to the error channel
92
+ * @returns true on success, false on failure
93
+ */
94
+ static async writeJsonFile(directoryPath, filename, data, sendErrorToErrorChannel = true) {
95
+ // Skip if data is an Error array
96
+ if (Array.isArray(data) && data.length === 1 && data[0] === 'Error') {
97
+ Log_1.Log.error(`Cannot save data for ${filename}: data contains 'Error'`);
98
+ return false;
99
+ }
100
+ try {
101
+ // Create directory structure recursively
102
+ const directories = directoryPath.split(path_1.default.sep).filter(Boolean);
103
+ let currentPath = '';
104
+ for (const dir of directories) {
105
+ currentPath = path_1.default.join(currentPath, dir);
106
+ try {
107
+ await promises_1.default.access(currentPath);
108
+ }
109
+ catch {
110
+ await promises_1.default.mkdir(currentPath, { recursive: true });
111
+ }
112
+ }
113
+ if (!filename || filename.trim() === '') {
114
+ Log_1.Log.error('Cannot write JSON file: empty filename');
115
+ return false;
116
+ }
117
+ const cleanFilename = filename.replace(/\.json$/i, '');
118
+ const filePath = path_1.default.join(directoryPath, `${cleanFilename}.json`);
119
+ const jsonContent = JSON.stringify(data, null, 2);
120
+ await promises_1.default.writeFile(filePath, jsonContent);
121
+ Log_1.Log.info(`Successfully wrote data to ${filePath}`);
122
+ return true;
123
+ }
124
+ catch (error) {
125
+ const cleanFilename = filename.replace(/\.json$/i, '') || 'unknown';
126
+ if (sendErrorToErrorChannel) {
127
+ await Bot_1.Bot.log.info(EmbedManager_1.EmbedManager.error(`Failed to write file ${directoryPath}/${cleanFilename}.json: ${error}`));
128
+ }
129
+ else {
130
+ Log_1.Log.error(`Failed to write file ${directoryPath}/${cleanFilename}.json: ${error}`);
131
+ }
132
+ return false;
133
+ }
134
+ }
135
+ }
136
+ exports.FileManager = FileManager;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SendableComponentBuilder = void 0;
4
+ const discord_js_1 = require("discord.js");
5
+ class SendableComponentBuilder {
6
+ static isSendableComponent(thing) {
7
+ return thing instanceof discord_js_1.EmbedBuilder || thing instanceof discord_js_1.ActionRowBuilder;
8
+ }
9
+ static build(base, content, components) {
10
+ if (content) {
11
+ base.content = content;
12
+ }
13
+ const comps = Array.isArray(components) ? components : [components].filter(Boolean);
14
+ for (const comp of comps) {
15
+ if (comp instanceof discord_js_1.EmbedBuilder) {
16
+ base.embeds = base.embeds || [];
17
+ base.embeds.push(comp);
18
+ }
19
+ if (comp instanceof discord_js_1.ActionRowBuilder) {
20
+ base.components = base.components || [];
21
+ base.components.push(comp);
22
+ }
23
+ }
24
+ return base;
25
+ }
26
+ static buildInteraction(contentOrComponent, component, ephemeral = false) {
27
+ let base = {};
28
+ // Case 1
29
+ if (typeof contentOrComponent === 'string' && !component) {
30
+ base = this.build(base, contentOrComponent);
31
+ }
32
+ // Case 2
33
+ if (typeof contentOrComponent !== 'string' && !component) {
34
+ base = this.build(base, null, contentOrComponent);
35
+ }
36
+ // Cas 3: content and component not null
37
+ if (contentOrComponent && typeof contentOrComponent === 'string' && component) {
38
+ base = this.build(base, contentOrComponent, component);
39
+ }
40
+ if (ephemeral) {
41
+ base.flags = [discord_js_1.MessageFlags.Ephemeral];
42
+ }
43
+ return base;
44
+ }
45
+ static buildMessage(contentOrComponent, component) {
46
+ let base = {};
47
+ // Case 1
48
+ if (typeof contentOrComponent === 'string' && !component) {
49
+ return this.build(base, contentOrComponent);
50
+ }
51
+ // Case 2
52
+ if (typeof contentOrComponent !== 'string' && !component) {
53
+ return this.build(base, null, contentOrComponent);
54
+ }
55
+ // Case 3
56
+ if (typeof contentOrComponent == 'string' && component) {
57
+ return this.build(base, contentOrComponent, component);
58
+ }
59
+ throw new Error("At least content or component need to be defined to build a message");
60
+ }
61
+ }
62
+ exports.SendableComponentBuilder = SendableComponentBuilder;