@spatulox/simplediscordbot 1.0.1
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/.env.example +2 -0
- package/LICENSE.md +21 -0
- package/README.md +1 -0
- package/dist/bot/Bot.js +107 -0
- package/dist/bot/BotEnv.js +29 -0
- package/dist/bot/BotLog.js +90 -0
- package/dist/bot/BotMessage.js +79 -0
- package/dist/cli/BaseCLI.js +165 -0
- package/dist/cli/GenerationCLI/ContextMenuGeneratorCLI.js +109 -0
- package/dist/cli/GenerationCLI/GenerationCLI.js +25 -0
- package/dist/cli/GenerationCLI/InteractionGeneratorCLI.js +20 -0
- package/dist/cli/GenerationCLI/ModalGeneratorCLI.js +166 -0
- package/dist/cli/GenerationCLI/SlashCommandsGeneratorCLI.js +221 -0
- package/dist/cli/GenerationCLI.js +36 -0
- package/dist/cli/GuildListManager.js +61 -0
- package/dist/cli/InputCLI.js +25 -0
- package/dist/cli/InteractionCLI/InteractionCLI.js +30 -0
- package/dist/cli/InteractionCLI/InteractionCLIManager.js +80 -0
- package/dist/cli/InteractionCLI.js +109 -0
- package/dist/cli/MainCLI.js +32 -0
- package/dist/cli/type/ContextMenuConfig.js +2 -0
- package/dist/cli/type/InteractionType.js +14 -0
- package/dist/cli/type/SlashCommandConfig.js +2 -0
- package/dist/index.js +31 -0
- package/dist/manager/FileManager.js +136 -0
- package/dist/manager/direct/UserManager.js +76 -0
- package/dist/manager/discord/ChannelManager.js +48 -0
- package/dist/manager/discord/DiscordRegex.js +114 -0
- package/dist/manager/discord/GuildManager.js +67 -0
- package/dist/manager/discord/InviteManager.js +89 -0
- package/dist/manager/discord/RoleManager.js +83 -0
- package/dist/manager/discord/UserManager.js +69 -0
- package/dist/manager/guild/ChannelManager/ForumChannelManager.js +33 -0
- package/dist/manager/guild/ChannelManager/GuildChannelList.js +20 -0
- package/dist/manager/guild/ChannelManager/GuildChannelManager.js +91 -0
- package/dist/manager/guild/ChannelManager/GuildMessageManager.js +85 -0
- package/dist/manager/guild/ChannelManager/GuildTextChannelManager.js +33 -0
- package/dist/manager/guild/ChannelManager/GuildVoiceChannelManager.js +33 -0
- package/dist/manager/guild/ChannelManager/NewsChannelManager.js +33 -0
- package/dist/manager/guild/ChannelManager/StageChannelManager.js +33 -0
- package/dist/manager/guild/ChannelManager/ThreadChannelManager.js +34 -0
- package/dist/manager/guild/GuildManager.js +134 -0
- package/dist/manager/guild/GuildUserManager.js +212 -0
- package/dist/manager/guild/InviteManager.js +68 -0
- package/dist/manager/guild/InviteManager_old.js +89 -0
- package/dist/manager/guild/RoleManager.js +83 -0
- package/dist/manager/hadlers_old/builder/ModalManager.js +111 -0
- package/dist/manager/hadlers_old/builder/delete.js +49 -0
- package/dist/manager/hadlers_old/builder/deploy.js +225 -0
- package/dist/manager/hadlers_old/builder/interactions/CommandManager.js +14 -0
- package/dist/manager/hadlers_old/builder/interactions/ContextMenuManager.js +14 -0
- package/dist/manager/hadlers_old/builder/interactions/InteractionBase.js +341 -0
- package/dist/manager/hadlers_old/builder/interactions/InteractionManager.js +65 -0
- package/dist/manager/hadlers_old/builder/list.js +166 -0
- package/dist/manager/messages/EmbedManager.js +131 -0
- package/dist/manager/messages/ReactionManager.js +99 -0
- package/dist/manager/messages/WebhookManager.js +105 -0
- package/dist/type/FolderName.js +9 -0
- package/dist/utils/DiscordRegex.js +116 -0
- package/dist/utils/Log.js +28 -0
- package/dist/utils/SimpleMutex.js +39 -0
- package/dist/utils/network/InternetChecker.js +54 -0
- package/dist/utils/times/UnitTime.js +85 -0
- package/package.json +40 -0
package/.env.example
ADDED
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Spatulox
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Simple Discord Bot
|
package/dist/bot/Bot.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
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.Bot = void 0;
|
|
7
|
+
const discord_js_1 = require("discord.js");
|
|
8
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
+
const UnitTime_1 = require("../utils/times/UnitTime");
|
|
10
|
+
const Log_1 = require("../utils/Log");
|
|
11
|
+
const InternetChecker_1 = require("../utils/network/InternetChecker");
|
|
12
|
+
const BotLog_1 = require("./BotLog");
|
|
13
|
+
const BotMessage_1 = require("./BotMessage");
|
|
14
|
+
const BotEnv_1 = require("./BotEnv");
|
|
15
|
+
dotenv_1.default.config();
|
|
16
|
+
class Bot {
|
|
17
|
+
get config() { return Bot._config; }
|
|
18
|
+
get client() { return Bot._client; }
|
|
19
|
+
static get client() { return Bot._client; }
|
|
20
|
+
static get config() { return Bot._config; }
|
|
21
|
+
constructor(client, config) {
|
|
22
|
+
Log_1.Log.info('----------------------------------------------------');
|
|
23
|
+
Log_1.Log.info("Starting Program");
|
|
24
|
+
Bot.criticConfig = { dev: BotEnv_1.BotEnv.dev, token: BotEnv_1.BotEnv.token };
|
|
25
|
+
Bot._config = { ...config, clientId: BotEnv_1.BotEnv.clientId };
|
|
26
|
+
Bot._client = client;
|
|
27
|
+
(async () => {
|
|
28
|
+
Log_1.Log.info(`Using discord.js version: ${discord_js_1.version}`);
|
|
29
|
+
Log_1.Log.info('Trying to connect to Discord Servers');
|
|
30
|
+
await InternetChecker_1.InternetChecker.checkConnection(3);
|
|
31
|
+
await this.login();
|
|
32
|
+
Bot._client.on(discord_js_1.Events.ClientReady, () => {
|
|
33
|
+
if (client && client.user) {
|
|
34
|
+
Log_1.Log.info(`${client.user.username} has logged in, waiting...`);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
/*Bot.client.on(Events.InteractionCreate, async (interaction: Interaction) =>{
|
|
38
|
+
console.log(interaction);
|
|
39
|
+
})*/
|
|
40
|
+
})();
|
|
41
|
+
}
|
|
42
|
+
async login(maxTries = 3) {
|
|
43
|
+
let success = false;
|
|
44
|
+
let tries = 0;
|
|
45
|
+
while (!success && tries < maxTries) {
|
|
46
|
+
try {
|
|
47
|
+
await Bot._client.login(Bot.criticConfig.token);
|
|
48
|
+
success = true;
|
|
49
|
+
Bot._client.once(discord_js_1.Events.ClientReady, () => {
|
|
50
|
+
if (Bot._client.user) {
|
|
51
|
+
BotLog_1.BotLog.initDiscordLogging();
|
|
52
|
+
Log_1.Log.info(`Connected as ${Bot._client.user.tag}`);
|
|
53
|
+
Log_1.Log.info(`Connected on : ${Bot._client.guilds.cache.size} servers`);
|
|
54
|
+
Bot._client.guilds.cache.forEach(g => console.log(` - ${g.name}`));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
Log_1.Log.error(`Connection error : ${error}. Trying again...`);
|
|
60
|
+
tries++;
|
|
61
|
+
await new Promise(resolve => setTimeout(resolve, UnitTime_1.Time.second.SEC_03.toMilliseconds()));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!success) {
|
|
65
|
+
Log_1.Log.error('Impossible to connect the bot after 3 attempts');
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
static setActivity(message, type) {
|
|
71
|
+
if (Bot._client.user) {
|
|
72
|
+
Bot._client.user.setActivity({ name: message, type });
|
|
73
|
+
Log_1.Log.info(`Activity defined : ${message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
static setRandomActivity(randomActivity, intervalMs = null) {
|
|
77
|
+
if (randomActivity.length == 0) {
|
|
78
|
+
Log_1.Log.error("Bot.randomActivity = [{}] is empty");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const pickRandom = () => {
|
|
82
|
+
const random = randomActivity[Math.floor(Math.random() * randomActivity.length)];
|
|
83
|
+
Bot.setActivity(random.message, random.type);
|
|
84
|
+
return random;
|
|
85
|
+
};
|
|
86
|
+
if (intervalMs === null) {
|
|
87
|
+
pickRandom();
|
|
88
|
+
Log_1.Log.info(`Activity set ONCE`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (intervalMs != null) {
|
|
92
|
+
setInterval(async () => {
|
|
93
|
+
const random = randomActivity[Math.floor(Math.random() * randomActivity.length)];
|
|
94
|
+
Bot.setActivity(random.message, random.type);
|
|
95
|
+
}, intervalMs);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
pickRandom();
|
|
99
|
+
setInterval(() => {
|
|
100
|
+
pickRandom();
|
|
101
|
+
}, intervalMs);
|
|
102
|
+
Log_1.Log.info(`Random activity started (every ${Math.round(intervalMs / 60000)}min)`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.Bot = Bot;
|
|
106
|
+
Bot.log = new BotLog_1.BotLog();
|
|
107
|
+
Bot.message = new BotMessage_1.BotMessage();
|
|
@@ -0,0 +1,29 @@
|
|
|
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.BotEnv = void 0;
|
|
7
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
8
|
+
dotenv_1.default.config();
|
|
9
|
+
exports.BotEnv = {
|
|
10
|
+
get token() {
|
|
11
|
+
const token = process.env.DISCORD_BOT_TOKEN;
|
|
12
|
+
if (!token)
|
|
13
|
+
throw new Error('Missing environment variable : DISCORD_BOT_TOKEN');
|
|
14
|
+
return token;
|
|
15
|
+
},
|
|
16
|
+
get dev() {
|
|
17
|
+
return process.env.DEV === 'true';
|
|
18
|
+
},
|
|
19
|
+
get clientId() {
|
|
20
|
+
const token = process.env.DISCORD_BOT_CLIENTID;
|
|
21
|
+
if (!token)
|
|
22
|
+
throw new Error('Missing environment variable : DISCORD_BOT_CLIENTID');
|
|
23
|
+
const discordIdRegex = /^[0-9]{19}$/;
|
|
24
|
+
if (!discordIdRegex.test(token)) {
|
|
25
|
+
throw new Error("Invalid token format");
|
|
26
|
+
}
|
|
27
|
+
return token;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
this.logChannel = null;
|
|
11
|
+
this.errorChannel = null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Initialize Discord logging channels and update Bot.log references
|
|
15
|
+
*/
|
|
16
|
+
static initDiscordLogging() {
|
|
17
|
+
// Find log channel
|
|
18
|
+
if (Bot_1.Bot.config.logChannelId) {
|
|
19
|
+
const logCh = Bot_1.Bot.client.channels.cache.get(Bot_1.Bot.config.logChannelId);
|
|
20
|
+
if (logCh && logCh.isTextBased()) {
|
|
21
|
+
Bot_1.Bot.log.logChannel = logCh;
|
|
22
|
+
Log_1.Log.info(`Discord log channel initialized: ${Bot_1.Bot.log.logChannel.name}`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
Log_1.Log.warn(`Discord Log channel ${Bot_1.Bot.config.logChannelId} not found or not accessible`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (Bot_1.Bot.config.errorChannelId) {
|
|
29
|
+
// Find error channel
|
|
30
|
+
const errorCh = Bot_1.Bot.client.channels.cache.get(Bot_1.Bot.config.errorChannelId);
|
|
31
|
+
if (errorCh && errorCh.isTextBased()) {
|
|
32
|
+
Bot_1.Bot.log.errorChannel = errorCh;
|
|
33
|
+
Log_1.Log.info(`Discord error channel initialized: ${Bot_1.Bot.log.errorChannel.name}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
Log_1.Log.warn(`Discord Error channel ${Bot_1.Bot.config.errorChannelId} not found or not accessible`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Send content to specific Discord channel
|
|
42
|
+
*/
|
|
43
|
+
async _sendToChannel(channel, content, prefix = 'info') {
|
|
44
|
+
if (!channel)
|
|
45
|
+
return;
|
|
46
|
+
let msg;
|
|
47
|
+
try {
|
|
48
|
+
if (content instanceof discord_js_1.EmbedBuilder) {
|
|
49
|
+
msg = await channel.send({ embeds: [content] });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const timestamp = `\`${new Date().toISOString()}\``;
|
|
53
|
+
msg = await channel.send(`${prefix.toUpperCase()} ${timestamp} ${content}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
Log_1.Log.error(`Failed to send to Discord channel: ${error}`);
|
|
58
|
+
}
|
|
59
|
+
return msg;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Send INFO log - TEXT or EMBED !
|
|
63
|
+
*/
|
|
64
|
+
async sendLog(content) {
|
|
65
|
+
Log_1.Log.info(content instanceof discord_js_1.EmbedBuilder ? 'Embed logged' : content);
|
|
66
|
+
return await this._sendToChannel(this.logChannel, content, 'info');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Send ERROR log - TEXT or EMBED !
|
|
70
|
+
*/
|
|
71
|
+
async sendError(content) {
|
|
72
|
+
Log_1.Log.error(content instanceof discord_js_1.EmbedBuilder ? 'Embed error logged' : content);
|
|
73
|
+
return await this._sendToChannel(this.errorChannel, content, 'error');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Send WARNING log - TEXT or EMBED (log channel)
|
|
77
|
+
*/
|
|
78
|
+
async sendWarn(content) {
|
|
79
|
+
Log_1.Log.warn(content instanceof discord_js_1.EmbedBuilder ? 'Embed warning logged' : content);
|
|
80
|
+
return await this._sendToChannel(this.logChannel, content, 'warn');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Send DEBUG log - TEXT or EMBED (log channel)
|
|
84
|
+
*/
|
|
85
|
+
async sendDebug(content) {
|
|
86
|
+
Log_1.Log.debug(content instanceof discord_js_1.EmbedBuilder ? 'Embed debug logged' : content);
|
|
87
|
+
return await this._sendToChannel(this.logChannel, content, 'debug');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.BotLog = BotLog;
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
class BotMessage {
|
|
9
|
+
/**
|
|
10
|
+
* Send message to any text-based channel
|
|
11
|
+
*/
|
|
12
|
+
async send(channel, content) {
|
|
13
|
+
if (!channel) {
|
|
14
|
+
Log_1.Log.warn('Cannot send message: invalid channel');
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (typeof channel == "string") {
|
|
18
|
+
channel = Bot_1.Bot.client.channels.cache.get(channel);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
if (content instanceof discord_js_1.EmbedBuilder) {
|
|
22
|
+
const msg = await channel.send({ embeds: [content] });
|
|
23
|
+
Log_1.Log.info(`Embed sent to ${channel.id}`);
|
|
24
|
+
return msg;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const msg = await channel.send(content);
|
|
28
|
+
Log_1.Log.info(`Message sent to ${channel.id}: ${content.slice(0, 50)}...`);
|
|
29
|
+
return msg;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
Log_1.Log.error(`Failed to send message to ${channel.id}: ${error}`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async sendDM(user, content) {
|
|
38
|
+
try {
|
|
39
|
+
let targetUser;
|
|
40
|
+
if (user instanceof discord_js_1.User || user instanceof discord_js_1.GuildMember) {
|
|
41
|
+
targetUser = user instanceof discord_js_1.GuildMember ? user.user : user;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
targetUser = await Bot_1.Bot.client.users.fetch(user);
|
|
45
|
+
}
|
|
46
|
+
if (content instanceof discord_js_1.EmbedBuilder) {
|
|
47
|
+
return await targetUser.send({ embeds: [content] });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return await targetUser.send(content);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
Bot_1.Bot.log.sendError(`Failed to send message to ${user}: ${error}`);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Quick success message
|
|
60
|
+
*/
|
|
61
|
+
success(channel, message) {
|
|
62
|
+
const embed = EmbedManager_1.EmbedManager.success(message);
|
|
63
|
+
if (channel instanceof discord_js_1.User || channel instanceof discord_js_1.GuildMember) {
|
|
64
|
+
return this.sendDM(channel, embed);
|
|
65
|
+
}
|
|
66
|
+
return this.send(channel, embed);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Quick error message
|
|
70
|
+
*/
|
|
71
|
+
error(channel, message) {
|
|
72
|
+
const embed = EmbedManager_1.EmbedManager.error(message);
|
|
73
|
+
if (channel instanceof discord_js_1.User || channel instanceof discord_js_1.GuildMember) {
|
|
74
|
+
return this.sendDM(channel, embed);
|
|
75
|
+
}
|
|
76
|
+
return this.send(channel, embed);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.BotMessage = BotMessage;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.BaseCLI = void 0;
|
|
8
|
+
const readline_1 = __importDefault(require("readline"));
|
|
9
|
+
const FileManager_1 = require("../manager/FileManager");
|
|
10
|
+
/**
|
|
11
|
+
* --- BaseCLI ---
|
|
12
|
+
*/
|
|
13
|
+
class BaseCLI {
|
|
14
|
+
get rl() {
|
|
15
|
+
if (!BaseCLI._rl) {
|
|
16
|
+
BaseCLI._rl = readline_1.default.createInterface({
|
|
17
|
+
input: process.stdin,
|
|
18
|
+
output: process.stdout
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return BaseCLI._rl;
|
|
22
|
+
}
|
|
23
|
+
constructor(parent) {
|
|
24
|
+
this.parent = parent;
|
|
25
|
+
}
|
|
26
|
+
getTitle() {
|
|
27
|
+
return "BaseCLI";
|
|
28
|
+
}
|
|
29
|
+
async showMainMenu() {
|
|
30
|
+
console.clear();
|
|
31
|
+
console.log(this.getTitle());
|
|
32
|
+
console.log('â'.repeat(40));
|
|
33
|
+
this.menuSelection.forEach((option, index) => {
|
|
34
|
+
console.log(`${index + 1}. ${option.label}`);
|
|
35
|
+
});
|
|
36
|
+
console.log('â'.repeat(40));
|
|
37
|
+
const choice = await this.prompt('Choose an option: ');
|
|
38
|
+
if (choice == "exit") {
|
|
39
|
+
return this.goBack();
|
|
40
|
+
}
|
|
41
|
+
const choiceIndex = parseInt(choice) - 1;
|
|
42
|
+
if (choiceIndex >= 0 && choiceIndex < this.menuSelection.length) {
|
|
43
|
+
const option = this.menuSelection[choiceIndex];
|
|
44
|
+
if (!option) {
|
|
45
|
+
console.log("Invalid Choice");
|
|
46
|
+
return this.showMainMenu();
|
|
47
|
+
}
|
|
48
|
+
const result = await option.action();
|
|
49
|
+
if (result instanceof BaseCLI) {
|
|
50
|
+
if (result == this) {
|
|
51
|
+
await this.execute();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return await result.showMainMenu();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
await this.prompt('Press Enter to continue...');
|
|
59
|
+
return this.showMainMenu();
|
|
60
|
+
}
|
|
61
|
+
async prompt(question) {
|
|
62
|
+
return new Promise(resolve => this.rl.question(question, resolve));
|
|
63
|
+
}
|
|
64
|
+
async requireInput(message, validator, canBeEmpty = false) {
|
|
65
|
+
while (true) {
|
|
66
|
+
const value = (await this.prompt(message));
|
|
67
|
+
if (!value && !canBeEmpty) {
|
|
68
|
+
console.log("â ī¸ This field is required. Please enter a value.");
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (validator && !validator(value)) {
|
|
72
|
+
console.log("â ī¸ Invalid input. Try again.");
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async yesNoInput(message) {
|
|
79
|
+
while (true) {
|
|
80
|
+
const value = (await this.prompt(message));
|
|
81
|
+
if (!value) {
|
|
82
|
+
console.log("â ī¸ This field is required. Please enter a value.");
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (!["y", "n", "yes", "no"].includes(value.toLowerCase())) {
|
|
86
|
+
console.log("â ī¸ Invalid input. Try again.");
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
return value == "y" || value == "yes";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async showHelp() {
|
|
93
|
+
console.clear();
|
|
94
|
+
console.log('');
|
|
95
|
+
console.log('||| HELP - Discord Bot Command Manager CLI |||');
|
|
96
|
+
console.log('');
|
|
97
|
+
console.log('đ Wiki: https://github.com/Spatulox/SimpleDiscordBot/wiki');
|
|
98
|
+
console.log('â'.repeat(80));
|
|
99
|
+
console.log('đ¤ What it does:');
|
|
100
|
+
console.log(' âĸ Manage your Discord interactions (slash commands & context menus) via an interactive CLI');
|
|
101
|
+
console.log(' âĸ Let you deploy/update/delete any interaction');
|
|
102
|
+
console.log(' âĸ Let you generate an interaction files');
|
|
103
|
+
console.log('');
|
|
104
|
+
console.log('How you need to save your interaction files');
|
|
105
|
+
console.log('đ Folder Structure:');
|
|
106
|
+
console.log(' âââ handlers/ â In the root folder of your project');
|
|
107
|
+
console.log(' â âââ commands/ â Slash Commands (type 1)');
|
|
108
|
+
console.log(' â âââ context_menu/ â Context Menus (type 2/3)');
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log('đ¯ Features:');
|
|
111
|
+
console.log(' đ 1. List Remote â Show deployed commands on Discord');
|
|
112
|
+
console.log(' đ 2. Deploy Local â Deploy local JSON files â Discord');
|
|
113
|
+
console.log(' đ 3. Update Remote â Update Discord commands based on local JSON file');
|
|
114
|
+
console.log(' đī¸ 4. Delete Remote â Remove Discord commands based on local JSON file');
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log('đŽ Selection:');
|
|
117
|
+
console.log(' âĸ Numbered lists appear after the interaction list');
|
|
118
|
+
console.log(' âĸ Enter: "1,3,5" or "all" to select which interaction you want to apply the action');
|
|
119
|
+
console.log('');
|
|
120
|
+
console.log('đ Wiki: https://github.com/Spatulox/SimpleDiscordBot/wiki');
|
|
121
|
+
console.log('â'.repeat(80));
|
|
122
|
+
await this.prompt('Press Enter to continue...');
|
|
123
|
+
if (this.parent) {
|
|
124
|
+
await this.parent.showMainMenu();
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
await this.showMainMenu();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async goBack() {
|
|
131
|
+
if (this.parent) {
|
|
132
|
+
return this.parent?.showMainMenu();
|
|
133
|
+
}
|
|
134
|
+
return this.execute(); // Fallback for MainCLI
|
|
135
|
+
}
|
|
136
|
+
async saveFile(folderName, filename, data) {
|
|
137
|
+
let finalFilename = filename;
|
|
138
|
+
if (await FileManager_1.FileManager.readJsonFile(`./handlers/${folderName}/${filename}`)) {
|
|
139
|
+
if (!await this.yesNoInput(`"${finalFilename}" already exists. Overwrite? (y/n): `)) {
|
|
140
|
+
const timestamp = Date.now();
|
|
141
|
+
finalFilename = `${filename.replace('.json', '')}-${timestamp}`;
|
|
142
|
+
console.log(`đ New filename: ${finalFilename}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// 2. Preview + Confirmation
|
|
146
|
+
console.clear();
|
|
147
|
+
console.log("⨠Final JSON preview:");
|
|
148
|
+
console.log(JSON.stringify(data, null, 2));
|
|
149
|
+
if (!await this.yesNoInput("\nSave this file? (y/n): ")) {
|
|
150
|
+
console.log("Cancelled");
|
|
151
|
+
await this.prompt('Press Enter to continue...');
|
|
152
|
+
return this.showMainMenu();
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
await FileManager_1.FileManager.writeJsonFile(`./handlers/${folderName}`, finalFilename, data, false);
|
|
156
|
+
console.log(`File saved: ./handlers/${folderName}/${finalFilename}`);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error("Error saving file:", error);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.BaseCLI = BaseCLI;
|
|
165
|
+
BaseCLI._rl = null;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContextMenuGeneratorCLI = void 0;
|
|
4
|
+
const BaseCLI_1 = require("../BaseCLI");
|
|
5
|
+
const discord_js_1 = require("discord.js");
|
|
6
|
+
const DiscordRegex_1 = require("../../utils/DiscordRegex");
|
|
7
|
+
const FolderName_1 = require("../../type/FolderName");
|
|
8
|
+
class ContextMenuGeneratorCLI extends BaseCLI_1.BaseCLI {
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.menuSelection = [
|
|
12
|
+
{ label: "Generate Context Menu", action: () => this },
|
|
13
|
+
{ label: "Back", action: () => this.goBack() },
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
getTitle() {
|
|
17
|
+
return "đ Context Menu JSON Generator";
|
|
18
|
+
}
|
|
19
|
+
async execute() {
|
|
20
|
+
const config = { dm_permission: false, integration_types: [], name: "", type: 2 };
|
|
21
|
+
// 1. Context Menu Name
|
|
22
|
+
console.clear();
|
|
23
|
+
console.log("đ¤ 1/10 - Name");
|
|
24
|
+
config.name = await this.requireInput("Enter Context Menu name (ex: 'Example context Menu'): ");
|
|
25
|
+
// 2. Type (2=User, 3=Message)
|
|
26
|
+
console.clear();
|
|
27
|
+
console.log("đ§ 2/10 - Type");
|
|
28
|
+
console.log("2 => Context Menu for users");
|
|
29
|
+
console.log("3 => Context Menu for messages");
|
|
30
|
+
config.type = parseInt(await this.requireInput("Enter type (2 or 3): ", val => ["2", "3"].includes(val)));
|
|
31
|
+
// 3. Member Permissions
|
|
32
|
+
console.clear();
|
|
33
|
+
console.log("đ 3/10 - Permissions");
|
|
34
|
+
console.log("đ Valid permissions:\n", Object.keys(discord_js_1.PermissionFlagsBits).join(', '));
|
|
35
|
+
const permsInput = await this.requireInput("Default member permissions (comma separated, or 'none'): ", (val) => {
|
|
36
|
+
if (!val || val.toLowerCase() === "none" || val == '')
|
|
37
|
+
return true;
|
|
38
|
+
const permissions = val.split(",").map(p => p.trim());
|
|
39
|
+
const invalidPerms = permissions.filter(perm => !(perm in discord_js_1.PermissionFlagsBits));
|
|
40
|
+
if (invalidPerms.length > 0) {
|
|
41
|
+
console.log(`â Invalid permissions: ${invalidPerms.join(', ')}`);
|
|
42
|
+
console.log("đ Valid permissions:\n", Object.keys(discord_js_1.PermissionFlagsBits).join(', '));
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}, true);
|
|
47
|
+
if (permsInput !== '' && permsInput.toLowerCase() !== "none") {
|
|
48
|
+
config.default_member_permissions = permsInput.split(",")
|
|
49
|
+
.map(p => p.trim());
|
|
50
|
+
}
|
|
51
|
+
// 4. DM Permission
|
|
52
|
+
console.clear();
|
|
53
|
+
console.log("đŦ 4/10 - DM Permission");
|
|
54
|
+
config.dm_permission = (await this.yesNoInput("Allow in DMs? (y/n): "));
|
|
55
|
+
// 5. Integration Types
|
|
56
|
+
console.clear();
|
|
57
|
+
console.log("đ 5/10 - Integration Types");
|
|
58
|
+
console.log("0 => GUILD_INSTALL");
|
|
59
|
+
console.log("1 => USER_INSTALL");
|
|
60
|
+
console.log("(comma separated, or 'all')");
|
|
61
|
+
const intInput = await this.requireInput("Integration types: ");
|
|
62
|
+
if (intInput.toLowerCase() === "all") {
|
|
63
|
+
config.integration_types = [0, 1];
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
config.integration_types = intInput.split(",").map(i => parseInt(i.trim())).filter(i => !isNaN(i));
|
|
67
|
+
}
|
|
68
|
+
console.clear();
|
|
69
|
+
console.log("đ 6/10 - Contexts");
|
|
70
|
+
console.log("0 => Can be used inside server");
|
|
71
|
+
console.log("1 => Can be used inside DMs with bot");
|
|
72
|
+
console.log("2 => Group DMs & other DMs");
|
|
73
|
+
console.log("(comma separated, or 'all')");
|
|
74
|
+
const ctxInput = await this.requireInput("Contexts: ");
|
|
75
|
+
if (ctxInput.toLowerCase() === "all") {
|
|
76
|
+
config.contexts = [0, 1, 2];
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
config.contexts = ctxInput.split(",").map(i => parseInt(i.trim())).filter(i => !isNaN(i));
|
|
80
|
+
}
|
|
81
|
+
// 7. Guild IDs
|
|
82
|
+
console.clear();
|
|
83
|
+
console.log("đ 7/10 - Guild IDs (optional)");
|
|
84
|
+
console.log(" => Used to deploy specific context menu for specific guild");
|
|
85
|
+
const guildInput = await this.requireInput("Guild IDs (comma separated, or 'none'): ", (val) => {
|
|
86
|
+
if (!val || val.toLowerCase() === "none")
|
|
87
|
+
return true;
|
|
88
|
+
const ids = val.split(",").map(id => id.trim());
|
|
89
|
+
const invalidIds = ids.filter(id => !DiscordRegex_1.DiscordRegex.GUILD_ID.test(id));
|
|
90
|
+
if (invalidIds.length > 0) {
|
|
91
|
+
console.log(`â Invalid Guild IDs: ${invalidIds.join(', ')}`);
|
|
92
|
+
console.log("âšī¸ Discord Guild ID = 18 chiffres (ex: 1111160769132896377)");
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
console.log("â
Valid Guild IDs:", ids.join(', '));
|
|
96
|
+
return true;
|
|
97
|
+
}, true);
|
|
98
|
+
if (guildInput.toLowerCase() !== "none" && guildInput !== "") {
|
|
99
|
+
config.guildID = guildInput.split(",").map(id => id.trim());
|
|
100
|
+
}
|
|
101
|
+
// 8. Filename
|
|
102
|
+
console.clear();
|
|
103
|
+
console.log("đž 8/10 - File");
|
|
104
|
+
const filename = (await this.requireInput("Filename (ex: my-context-menu): ")) + ".json";
|
|
105
|
+
await this.saveFile(FolderName_1.FolderName.CONTEXT_MENU, filename, config);
|
|
106
|
+
return this.showMainMenu();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.ContextMenuGeneratorCLI = ContextMenuGeneratorCLI;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GenerationCLI = void 0;
|
|
4
|
+
const BaseCLI_1 = require("../BaseCLI");
|
|
5
|
+
const ContextMenuGeneratorCLI_1 = require("./ContextMenuGeneratorCLI");
|
|
6
|
+
const ModalGeneratorCLI_1 = require("./ModalGeneratorCLI");
|
|
7
|
+
const SlashCommandsGeneratorCLI_1 = require("./SlashCommandsGeneratorCLI");
|
|
8
|
+
class GenerationCLI extends BaseCLI_1.BaseCLI {
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.menuSelection = [
|
|
12
|
+
{ label: 'Generate Slash Command Template', action: () => new SlashCommandsGeneratorCLI_1.SlashCommandGeneratorCLI(this) },
|
|
13
|
+
{ label: 'Generate Context Menu Template', action: () => new ContextMenuGeneratorCLI_1.ContextMenuGeneratorCLI(this) },
|
|
14
|
+
{ label: 'Generate Modal Template', action: () => new ModalGeneratorCLI_1.ModalGeneratorCLI(this) },
|
|
15
|
+
{ label: 'Back', action: () => this.goBack() },
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
getTitle() {
|
|
19
|
+
return 'âī¸ Generation Manager CLI';
|
|
20
|
+
}
|
|
21
|
+
async execute() {
|
|
22
|
+
throw new Error("Method not implemented.");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.GenerationCLI = GenerationCLI;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InteractionGeneratorCLI = void 0;
|
|
4
|
+
const BaseCLI_1 = require("../BaseCLI");
|
|
5
|
+
class InteractionGeneratorCLI extends BaseCLI_1.BaseCLI {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.menuSelection = [
|
|
9
|
+
{ label: "Generate Modal", action: () => this },
|
|
10
|
+
{ label: 'Back', action: () => this, onSelect: () => this.goBack() },
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
getTitle() {
|
|
14
|
+
return "đ Interaction JSON Generator";
|
|
15
|
+
}
|
|
16
|
+
async action() {
|
|
17
|
+
throw new Error("Method not implemented.");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.InteractionGeneratorCLI = InteractionGeneratorCLI;
|