reciple 5.4.0 → 5.4.1-pre.3
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/bin/{bin.js → cjs/bin.js} +0 -0
- package/bin/{index.js → cjs/index.js} +0 -0
- package/bin/cjs/package.json +3 -0
- package/bin/{reciple → cjs/reciple}/classes/CommandCooldownManager.js +0 -0
- package/bin/{reciple → cjs/reciple}/classes/MessageCommandOptionManager.js +0 -0
- package/bin/{reciple → cjs/reciple}/classes/RecipleClient.js +0 -0
- package/bin/{reciple → cjs/reciple}/classes/RecipleConfig.js +0 -0
- package/bin/{reciple → cjs/reciple}/classes/builders/MessageCommandBuilder.js +0 -0
- package/bin/{reciple → cjs/reciple}/classes/builders/MessageCommandOptionBuilder.js +0 -0
- package/bin/{reciple → cjs/reciple}/classes/builders/SlashCommandBuilder.js +0 -0
- package/bin/{reciple → cjs/reciple}/flags.js +0 -0
- package/bin/{reciple → cjs/reciple}/logger.js +0 -0
- package/bin/{reciple → cjs/reciple}/modules.js +0 -0
- package/bin/{reciple → cjs/reciple}/permissions.js +0 -0
- package/bin/{reciple → cjs/reciple}/registerApplicationCommands.js +0 -0
- package/bin/{reciple → cjs/reciple}/types/builders.js +0 -0
- package/bin/{reciple → cjs/reciple}/types/commands.js +0 -0
- package/bin/{reciple → cjs/reciple}/types/paramOptions.js +0 -0
- package/bin/{reciple → cjs/reciple}/util.js +0 -0
- package/bin/{reciple → cjs/reciple}/version.js +0 -0
- package/bin/mjs/bin.js +46 -0
- package/bin/{index.d.ts → mjs/index.js} +0 -0
- package/bin/mjs/package.json +3 -0
- package/bin/mjs/reciple/classes/CommandCooldownManager.js +87 -0
- package/bin/mjs/reciple/classes/MessageCommandOptionManager.js +21 -0
- package/bin/mjs/reciple/classes/RecipleClient.js +363 -0
- package/bin/mjs/reciple/classes/RecipleConfig.js +92 -0
- package/bin/mjs/reciple/classes/builders/MessageCommandBuilder.js +218 -0
- package/bin/mjs/reciple/classes/builders/MessageCommandOptionBuilder.js +67 -0
- package/bin/mjs/reciple/classes/builders/SlashCommandBuilder.js +204 -0
- package/bin/mjs/reciple/flags.js +28 -0
- package/bin/mjs/reciple/logger.js +28 -0
- package/bin/mjs/reciple/modules.js +81 -0
- package/bin/mjs/reciple/permissions.js +23 -0
- package/bin/mjs/reciple/registerApplicationCommands.js +47 -0
- package/bin/mjs/reciple/types/builders.js +8 -0
- package/bin/mjs/reciple/types/commands.js +12 -0
- package/bin/mjs/reciple/types/paramOptions.js +1 -0
- package/bin/mjs/reciple/util.js +7 -0
- package/bin/mjs/reciple/version.js +38 -0
- package/bin/{bin.d.ts → types/bin.d.ts} +0 -0
- package/bin/types/index.d.ts +17 -0
- package/bin/{reciple → types/reciple}/classes/CommandCooldownManager.d.ts +0 -0
- package/bin/{reciple → types/reciple}/classes/MessageCommandOptionManager.d.ts +0 -0
- package/bin/{reciple → types/reciple}/classes/RecipleClient.d.ts +0 -0
- package/bin/{reciple → types/reciple}/classes/RecipleConfig.d.ts +0 -0
- package/bin/{reciple → types/reciple}/classes/builders/MessageCommandBuilder.d.ts +0 -0
- package/bin/{reciple → types/reciple}/classes/builders/MessageCommandOptionBuilder.d.ts +0 -0
- package/bin/{reciple → types/reciple}/classes/builders/SlashCommandBuilder.d.ts +0 -0
- package/bin/{reciple → types/reciple}/flags.d.ts +0 -0
- package/bin/{reciple → types/reciple}/logger.d.ts +0 -0
- package/bin/{reciple → types/reciple}/modules.d.ts +0 -0
- package/bin/{reciple → types/reciple}/permissions.d.ts +0 -0
- package/bin/{reciple → types/reciple}/registerApplicationCommands.d.ts +0 -0
- package/bin/{reciple → types/reciple}/types/builders.d.ts +1 -1
- package/bin/{reciple → types/reciple}/types/commands.d.ts +0 -0
- package/bin/{reciple → types/reciple}/types/paramOptions.d.ts +0 -0
- package/bin/{reciple → types/reciple}/util.d.ts +0 -0
- package/bin/{reciple → types/reciple}/version.d.ts +0 -0
- package/package.json +13 -8
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/bin/mjs/bin.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { RecipleClient } from './reciple/classes/RecipleClient.js';
|
|
3
|
+
import { RecipleConfig } from './reciple/classes/RecipleConfig.js';
|
|
4
|
+
import { rawVersion } from './reciple/version.js';
|
|
5
|
+
import { existsSync, readdirSync } from 'fs.js';
|
|
6
|
+
import { cwd, flags } from './reciple/flags.js';
|
|
7
|
+
import { input } from 'fallout-utility.js';
|
|
8
|
+
import chalk from 'chalk.js';
|
|
9
|
+
import 'dotenv/config';
|
|
10
|
+
import { normalizeArray } from 'discord.js.js';
|
|
11
|
+
import path from 'path.js';
|
|
12
|
+
const allowedFiles = ['node_modules', 'reciple.yml', 'package.json'];
|
|
13
|
+
const configPath = path.join(cwd, './reciple.yml');
|
|
14
|
+
if (readdirSync(cwd).filter(f => !f.startsWith('.') && allowedFiles.indexOf(f)).length > 0 && !existsSync(flags.config ?? configPath)) {
|
|
15
|
+
const ask = (flags.yes ? 'y' : null) ?? input('This directory does not contain reciple.yml. Would you like to init axis here? [y/n] ') ?? '';
|
|
16
|
+
if (ask.toString().toLowerCase() !== 'y')
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
let configParser;
|
|
20
|
+
try {
|
|
21
|
+
configParser = new RecipleConfig(flags.config ?? configPath).parseConfig();
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
console.error(`${chalk.bold.red('Config Error')}: ${chalk.white(err.message)}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const config = configParser.getConfig();
|
|
28
|
+
const client = new RecipleClient({ config: config, ...config.client });
|
|
29
|
+
if (config.fileLogging.clientLogs)
|
|
30
|
+
client.logger.info('Starting Reciple client v' + rawVersion);
|
|
31
|
+
(async () => {
|
|
32
|
+
await client.startModules(normalizeArray(config.modulesFolder));
|
|
33
|
+
client.on('ready', async () => {
|
|
34
|
+
if (client.isClientLogsEnabled())
|
|
35
|
+
client.logger.warn(`Logged in as ${client.user?.tag || 'Unknown'}!`);
|
|
36
|
+
client.on('cacheSweep', () => {
|
|
37
|
+
client.cooldowns.clean();
|
|
38
|
+
});
|
|
39
|
+
await client.loadModules();
|
|
40
|
+
client.addCommandListeners();
|
|
41
|
+
});
|
|
42
|
+
client.login(config.token).catch(err => {
|
|
43
|
+
if (client.isClientLogsEnabled())
|
|
44
|
+
client.logger.error(err);
|
|
45
|
+
});
|
|
46
|
+
})();
|
|
File without changes
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { normalizeArray } from 'discord.js.js';
|
|
2
|
+
/**
|
|
3
|
+
* cooled-down users manager
|
|
4
|
+
*/
|
|
5
|
+
export class CommandCooldownManager extends Array {
|
|
6
|
+
constructor(...data) {
|
|
7
|
+
super(...normalizeArray(data));
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Alias for `CommandCooldownManager#push()`
|
|
11
|
+
* @param options Cooled-down user data
|
|
12
|
+
*/
|
|
13
|
+
add(...options) {
|
|
14
|
+
return this.push(...options);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Remove cooldown from specific user, channel or guild
|
|
18
|
+
* @param options Remove cooldown data options
|
|
19
|
+
* @param limit Remove cooldown data limit
|
|
20
|
+
*/
|
|
21
|
+
remove(options, limit = 0) {
|
|
22
|
+
if (!Object.keys(options).length)
|
|
23
|
+
throw new TypeError('Provide atleast one option to remove cooldown data.');
|
|
24
|
+
let i = 0;
|
|
25
|
+
for (const index in this) {
|
|
26
|
+
if (!CommandCooldownManager.checkOptions(options, this[index]))
|
|
27
|
+
continue;
|
|
28
|
+
if (options.expireTime && this[index].expireTime > Date.now())
|
|
29
|
+
continue;
|
|
30
|
+
if (limit && i >= limit)
|
|
31
|
+
continue;
|
|
32
|
+
this.splice(Number(index));
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if the given user is cooled-down
|
|
38
|
+
* @param options Options to identify if user is on cooldown
|
|
39
|
+
*/
|
|
40
|
+
isCooledDown(options) {
|
|
41
|
+
const data = this.get(options);
|
|
42
|
+
if (!data)
|
|
43
|
+
return false;
|
|
44
|
+
this.remove({ ...data, channel: undefined, guild: undefined, type: undefined, command: undefined });
|
|
45
|
+
if (data.expireTime < Date.now())
|
|
46
|
+
return false;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Purge non cooled-down users from this array
|
|
51
|
+
* @param options Clean cooldown options
|
|
52
|
+
*/
|
|
53
|
+
clean(options) {
|
|
54
|
+
for (const index in this) {
|
|
55
|
+
if (options && !CommandCooldownManager.checkOptions(options, this[index]))
|
|
56
|
+
return;
|
|
57
|
+
if (this[index].expireTime > Date.now())
|
|
58
|
+
return;
|
|
59
|
+
this.slice(Number(index));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get someone's cooldown data
|
|
64
|
+
* @param options Get cooldown data options
|
|
65
|
+
*/
|
|
66
|
+
get(options) {
|
|
67
|
+
return this.find(data => CommandCooldownManager.checkOptions(options, data));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if the options are valid
|
|
71
|
+
* @param options Options to validated
|
|
72
|
+
* @param data Cooled-down user data
|
|
73
|
+
*/
|
|
74
|
+
static checkOptions(options, data) {
|
|
75
|
+
if (options?.user && options.user.id !== data.user.id)
|
|
76
|
+
return false;
|
|
77
|
+
if (options?.guild && options.guild.id !== data.guild?.id)
|
|
78
|
+
return false;
|
|
79
|
+
if (options?.channel && options.channel.id !== data.channel?.id)
|
|
80
|
+
return false;
|
|
81
|
+
if (options?.command && options.command !== data.command)
|
|
82
|
+
return false;
|
|
83
|
+
if (options?.type && options.type !== data.type)
|
|
84
|
+
return false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { normalizeArray } from 'discord.js.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validated message options manager
|
|
4
|
+
*/
|
|
5
|
+
export class MessageCommandOptionManager extends Array {
|
|
6
|
+
constructor(...data) {
|
|
7
|
+
super(...normalizeArray(data));
|
|
8
|
+
}
|
|
9
|
+
get(name, required) {
|
|
10
|
+
const option = this.find(o => o.name == name);
|
|
11
|
+
if (!option?.value == undefined && required)
|
|
12
|
+
throw new TypeError(`Can't find option named ${name}`);
|
|
13
|
+
return option ?? null;
|
|
14
|
+
}
|
|
15
|
+
getValue(name, requied) {
|
|
16
|
+
const option = this.get(name, requied);
|
|
17
|
+
if (!option?.value && requied)
|
|
18
|
+
throw new TypeError(`Value of option named ${name} is undefined`);
|
|
19
|
+
return option?.value ?? null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { MessageCommandBuilder, validateMessageCommandOptions } from './builders/MessageCommandBuilder.js';
|
|
2
|
+
import { SlashCommandBuilder } from './builders/SlashCommandBuilder.js';
|
|
3
|
+
import { CommandBuilderType } from '../types/builders.js';
|
|
4
|
+
import { registerApplicationCommands } from '../registerApplicationCommands.js';
|
|
5
|
+
import { CommandHaltReason } from '../types/commands.js';
|
|
6
|
+
import { botHasExecutePermissions, userHasCommandPermissions } from '../permissions.js';
|
|
7
|
+
import { CommandCooldownManager } from './CommandCooldownManager.js';
|
|
8
|
+
import { MessageCommandOptionManager } from './MessageCommandOptionManager.js';
|
|
9
|
+
import { getCommand } from 'fallout-utility.js';
|
|
10
|
+
import { RecipleConfig } from './RecipleConfig.js';
|
|
11
|
+
import { getModules } from '../modules.js';
|
|
12
|
+
import { createLogger } from '../logger.js';
|
|
13
|
+
import { version } from '../version.js';
|
|
14
|
+
import { cwd } from '../flags.js';
|
|
15
|
+
import path from 'path.js';
|
|
16
|
+
import { ChannelType, Client, normalizeArray } from 'discord.js.js';
|
|
17
|
+
export class RecipleClient extends Client {
|
|
18
|
+
config = RecipleConfig.getDefaultConfig();
|
|
19
|
+
commands = { slashCommands: {}, messageCommands: {} };
|
|
20
|
+
additionalApplicationCommands = [];
|
|
21
|
+
cooldowns = new CommandCooldownManager();
|
|
22
|
+
modules = [];
|
|
23
|
+
logger;
|
|
24
|
+
version = version;
|
|
25
|
+
/**
|
|
26
|
+
* @param options Client options
|
|
27
|
+
*/
|
|
28
|
+
constructor(options) {
|
|
29
|
+
super(options);
|
|
30
|
+
this.logger = createLogger(!!options.config?.fileLogging.stringifyLoggedJSON, options.config?.fileLogging.debugmode);
|
|
31
|
+
if (!options.config)
|
|
32
|
+
throw new Error('Config is not defined.');
|
|
33
|
+
this.config = { ...this.config, ...(options.config ?? {}) };
|
|
34
|
+
if (this.config.fileLogging.enabled)
|
|
35
|
+
this.logger.logFile(path.join(cwd, this.config.fileLogging.logFilePath ?? 'logs/latest.log'), false);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load modules from modules folder
|
|
39
|
+
* @param folders List of folders that contains the modules you want to load
|
|
40
|
+
*/
|
|
41
|
+
async startModules(...folders) {
|
|
42
|
+
folders = normalizeArray(folders).map(f => path.join(cwd, f));
|
|
43
|
+
for (const folder of folders) {
|
|
44
|
+
if (this.isClientLogsEnabled())
|
|
45
|
+
this.logger.info(`Loading Modules from ${folder}`);
|
|
46
|
+
const modules = await getModules(this, folder).catch(() => null);
|
|
47
|
+
if (!modules) {
|
|
48
|
+
if (this.isClientLogsEnabled())
|
|
49
|
+
this.logger.error(`Failed to load modules from ${folder}`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
this.modules.push(...modules.modules);
|
|
53
|
+
}
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Execute `onLoad()` from client modules and register application commands if enabled
|
|
58
|
+
*/
|
|
59
|
+
async loadModules() {
|
|
60
|
+
for (const m in this.modules) {
|
|
61
|
+
const module_ = this.modules[m];
|
|
62
|
+
if (typeof module_.script?.onLoad === 'function') {
|
|
63
|
+
await Promise.resolve(module_.script.onLoad(this)).catch(err => {
|
|
64
|
+
if (this.isClientLogsEnabled()) {
|
|
65
|
+
this.logger.error(`Error loading ${module_.info.filename ?? 'unknown module'}:`);
|
|
66
|
+
this.logger.error(err);
|
|
67
|
+
}
|
|
68
|
+
this.modules = this.modules.filter((_r, i) => i.toString() !== m.toString());
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (typeof module_.script?.commands !== 'undefined') {
|
|
72
|
+
for (const command of module_.script.commands) {
|
|
73
|
+
this.addCommand(command);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (this.isClientLogsEnabled()) {
|
|
78
|
+
this.logger.info(`${this.modules.length} modules loaded.`);
|
|
79
|
+
this.logger.info(`${Object.keys(this.commands.messageCommands).length} message commands loaded.`);
|
|
80
|
+
this.logger.info(`${Object.keys(this.commands.slashCommands).length} slash commands loaded.`);
|
|
81
|
+
}
|
|
82
|
+
if (this.config.commands.slashCommand.registerCommands) {
|
|
83
|
+
await registerApplicationCommands({
|
|
84
|
+
client: this,
|
|
85
|
+
commands: [...Object.values(this.commands.slashCommands), ...this.additionalApplicationCommands],
|
|
86
|
+
guilds: this.config.commands.slashCommand.guilds
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Add module
|
|
93
|
+
* @param options Module options
|
|
94
|
+
*/
|
|
95
|
+
async addModule(options) {
|
|
96
|
+
const { script } = options;
|
|
97
|
+
const registerCommands = options.registerApplicationCommands;
|
|
98
|
+
const info = options.moduleInfo;
|
|
99
|
+
this.modules.push({
|
|
100
|
+
script,
|
|
101
|
+
info: {
|
|
102
|
+
filename: undefined,
|
|
103
|
+
versions: typeof script.versions == 'string' ? [script.versions] : script.versions,
|
|
104
|
+
path: undefined,
|
|
105
|
+
...info
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
if (typeof script?.onLoad === 'function')
|
|
109
|
+
await Promise.resolve(script.onLoad(this));
|
|
110
|
+
if (this.isClientLogsEnabled())
|
|
111
|
+
this.logger.info(`${this.modules.length} modules loaded.`);
|
|
112
|
+
for (const command of script.commands ?? []) {
|
|
113
|
+
if (!command.name)
|
|
114
|
+
continue;
|
|
115
|
+
this.addCommand(command);
|
|
116
|
+
}
|
|
117
|
+
if (registerCommands)
|
|
118
|
+
await registerApplicationCommands({
|
|
119
|
+
client: this,
|
|
120
|
+
commands: [...Object.values(this.commands.slashCommands), ...this.additionalApplicationCommands],
|
|
121
|
+
guilds: this.config.commands.slashCommand.guilds
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Add slash or message command to client
|
|
126
|
+
* @param command Slash/Message command builder
|
|
127
|
+
*/
|
|
128
|
+
addCommand(command) {
|
|
129
|
+
if (command.type === CommandBuilderType.SlashCommand) {
|
|
130
|
+
this.commands.slashCommands[command.name] = SlashCommandBuilder.resolveSlashCommand(command);
|
|
131
|
+
}
|
|
132
|
+
else if (command.type === CommandBuilderType.MessageCommand) {
|
|
133
|
+
this.commands.messageCommands[command.name] = MessageCommandBuilder.resolveMessageCommand(command);
|
|
134
|
+
}
|
|
135
|
+
else if (this.isClientLogsEnabled()) {
|
|
136
|
+
this.logger.error(`Unknow command "${typeof command ?? 'unknown'}".`);
|
|
137
|
+
}
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Listed to command executions
|
|
142
|
+
*/
|
|
143
|
+
addCommandListeners() {
|
|
144
|
+
if (this.config.commands.messageCommand.enabled)
|
|
145
|
+
this.on('messageCreate', (message) => { this.messageCommandExecute(message); });
|
|
146
|
+
if (this.config.commands.slashCommand.enabled)
|
|
147
|
+
this.on('interactionCreate', (interaction) => { this.slashCommandExecute(interaction); });
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Execute a slash command
|
|
152
|
+
* @param interaction Slash command interaction
|
|
153
|
+
*/
|
|
154
|
+
async slashCommandExecute(interaction) {
|
|
155
|
+
if (!interaction || !interaction.isChatInputCommand() || !this.isReady())
|
|
156
|
+
return;
|
|
157
|
+
const command = this.findCommand(interaction.commandName, CommandBuilderType.SlashCommand);
|
|
158
|
+
if (!command)
|
|
159
|
+
return;
|
|
160
|
+
const executeData = {
|
|
161
|
+
interaction,
|
|
162
|
+
builder: command,
|
|
163
|
+
client: this
|
|
164
|
+
};
|
|
165
|
+
if (userHasCommandPermissions({
|
|
166
|
+
builder: command,
|
|
167
|
+
memberPermissions: interaction.memberPermissions ?? undefined,
|
|
168
|
+
commandPermissions: this.config.commands.slashCommand.permissions
|
|
169
|
+
})) {
|
|
170
|
+
if (!command)
|
|
171
|
+
return;
|
|
172
|
+
if (interaction.guild && !botHasExecutePermissions(interaction.guild, command.requiredBotPermissions)) {
|
|
173
|
+
if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.MissingBotPermissions })) {
|
|
174
|
+
await interaction.reply(this.getConfigMessage('insufficientBotPerms', 'Insufficient bot permissions.')).catch(er => this._replyError(er));
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const userCooldown = {
|
|
179
|
+
user: interaction.user,
|
|
180
|
+
command: command.name,
|
|
181
|
+
channel: interaction.channel ?? undefined,
|
|
182
|
+
guild: interaction.guild,
|
|
183
|
+
type: CommandBuilderType.SlashCommand
|
|
184
|
+
};
|
|
185
|
+
if (this.config.commands.slashCommand.enableCooldown && command.cooldown && !this.cooldowns.isCooledDown(userCooldown)) {
|
|
186
|
+
this.cooldowns.add({ ...userCooldown, expireTime: Date.now() + command.cooldown });
|
|
187
|
+
}
|
|
188
|
+
else if (this.config.commands.slashCommand.enableCooldown && command.cooldown) {
|
|
189
|
+
if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.Cooldown, ...this.cooldowns.get(userCooldown) })) {
|
|
190
|
+
await interaction.reply(this.getConfigMessage('cooldown', 'You cannot execute this command right now due to the cooldown.')).catch(er => this._replyError(er));
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
return this._executeCommand(command, executeData);
|
|
195
|
+
}
|
|
196
|
+
else if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.MissingMemberPermissions })) {
|
|
197
|
+
await interaction.reply(this.getConfigMessage('noPermissions', 'You do not have permission to use this command.')).catch(er => this._replyError(er));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Execute a Message command
|
|
202
|
+
* @param message Message command executor
|
|
203
|
+
* @param prefix Message command prefix
|
|
204
|
+
*/
|
|
205
|
+
async messageCommandExecute(message, prefix) {
|
|
206
|
+
if (!message.content || !this.isReady())
|
|
207
|
+
return;
|
|
208
|
+
const parseCommand = getCommand(message.content, prefix || this.config.commands.messageCommand.prefix || '!', this.config.commands.messageCommand.commandArgumentSeparator || ' ');
|
|
209
|
+
if (!parseCommand || !parseCommand?.command)
|
|
210
|
+
return;
|
|
211
|
+
const command = this.findCommand(parseCommand.command, CommandBuilderType.MessageCommand);
|
|
212
|
+
if (!command)
|
|
213
|
+
return;
|
|
214
|
+
const commandOptions = await validateMessageCommandOptions(command, parseCommand);
|
|
215
|
+
const executeData = {
|
|
216
|
+
message: message,
|
|
217
|
+
options: commandOptions,
|
|
218
|
+
command: parseCommand,
|
|
219
|
+
builder: command,
|
|
220
|
+
client: this
|
|
221
|
+
};
|
|
222
|
+
if (userHasCommandPermissions({
|
|
223
|
+
builder: command,
|
|
224
|
+
memberPermissions: message.member?.permissions,
|
|
225
|
+
commandPermissions: this.config.commands.messageCommand.permissions
|
|
226
|
+
})) {
|
|
227
|
+
if (!command.allowExecuteInDM && message.channel.type === ChannelType.DM || !command.allowExecuteByBots && (message.author.bot || message.author.system))
|
|
228
|
+
return;
|
|
229
|
+
if (command.validateOptions) {
|
|
230
|
+
if (commandOptions.some(o => o.invalid)) {
|
|
231
|
+
if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.InvalidArguments, invalidArguments: new MessageCommandOptionManager(...executeData.options.filter(o => o.invalid)) })) {
|
|
232
|
+
message.reply(this.getConfigMessage('invalidArguments', 'Invalid argument(s) given.')).catch(er => this._replyError(er));
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (commandOptions.some(o => o.missing)) {
|
|
237
|
+
if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.MissingArguments, missingArguments: new MessageCommandOptionManager(...executeData.options.filter(o => o.missing)) })) {
|
|
238
|
+
message.reply(this.getConfigMessage('missingArguments', 'Not enough arguments.')).catch(er => this._replyError(er));
|
|
239
|
+
}
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (message.guild && !botHasExecutePermissions(message.guild, command.requiredBotPermissions)) {
|
|
244
|
+
if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.MissingBotPermissions })) {
|
|
245
|
+
message.reply(this.getConfigMessage('insufficientBotPerms', 'Insufficient bot permissions.')).catch(er => this._replyError(er));
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const userCooldown = {
|
|
250
|
+
user: message.author,
|
|
251
|
+
command: command.name,
|
|
252
|
+
channel: message.channel,
|
|
253
|
+
guild: message.guild,
|
|
254
|
+
type: CommandBuilderType.MessageCommand
|
|
255
|
+
};
|
|
256
|
+
if (this.config.commands.messageCommand.enableCooldown && command.cooldown && !this.cooldowns.isCooledDown(userCooldown)) {
|
|
257
|
+
this.cooldowns.add({ ...userCooldown, expireTime: Date.now() + command.cooldown });
|
|
258
|
+
}
|
|
259
|
+
else if (this.config.commands.messageCommand.enableCooldown && command.cooldown) {
|
|
260
|
+
if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.Cooldown, ...this.cooldowns.get(userCooldown) })) {
|
|
261
|
+
await message.reply(this.getConfigMessage('cooldown', 'You cannot execute this command right now due to the cooldown.')).catch(er => this._replyError(er));
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
return this._executeCommand(command, executeData);
|
|
266
|
+
}
|
|
267
|
+
else if (!await this._haltCommand(command, { executeData, reason: CommandHaltReason.MissingMemberPermissions })) {
|
|
268
|
+
message.reply(this.getConfigMessage('noPermissions', 'You do not have permission to use this command.')).catch(er => this._replyError(er));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get a message from config
|
|
273
|
+
* @param messageKey Config messages key
|
|
274
|
+
* @param defaultMessage Default message when the key does not exists
|
|
275
|
+
*/
|
|
276
|
+
getConfigMessage(messageKey, defaultMessage) {
|
|
277
|
+
return this.config.messages[messageKey] ?? defaultMessage ?? messageKey;
|
|
278
|
+
}
|
|
279
|
+
findCommand(command, type) {
|
|
280
|
+
switch (type) {
|
|
281
|
+
case CommandBuilderType.SlashCommand:
|
|
282
|
+
return this.commands.slashCommands[command];
|
|
283
|
+
case CommandBuilderType.MessageCommand:
|
|
284
|
+
return this.commands.messageCommands[command.toLowerCase()]
|
|
285
|
+
?? (this.config.commands.messageCommand.allowCommandAlias
|
|
286
|
+
? Object.values(this.commands.messageCommands).find(c => c.aliases.some(a => a == command?.toLowerCase()))
|
|
287
|
+
: undefined);
|
|
288
|
+
default:
|
|
289
|
+
throw new TypeError('Unknown command type');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Returns true if client logs is enabled
|
|
294
|
+
*/
|
|
295
|
+
isClientLogsEnabled() {
|
|
296
|
+
return !!this.config.fileLogging.clientLogs;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Emits the "recipleReplyError" event
|
|
300
|
+
* @param error Received Error
|
|
301
|
+
*/
|
|
302
|
+
_replyError(error) {
|
|
303
|
+
this.emit('recipleReplyError', error);
|
|
304
|
+
}
|
|
305
|
+
async _haltCommand(command, haltData) {
|
|
306
|
+
try {
|
|
307
|
+
const haltResolved = (command.halt
|
|
308
|
+
? await (command.type == CommandBuilderType.SlashCommand
|
|
309
|
+
? Promise.resolve(command.halt(haltData))
|
|
310
|
+
: Promise.resolve(command.halt(haltData))).catch(err => { throw err; })
|
|
311
|
+
: false) ?? false;
|
|
312
|
+
this.emit('recipleCommandHalt', haltData);
|
|
313
|
+
return haltResolved;
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
if (this.isClientLogsEnabled()) {
|
|
317
|
+
this.logger.error(`An error occured executing command halt for "${command.name}"`);
|
|
318
|
+
this.logger.error(err);
|
|
319
|
+
}
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async _executeCommand(command, executeData) {
|
|
324
|
+
try {
|
|
325
|
+
await Promise.resolve(command.type === CommandBuilderType.SlashCommand
|
|
326
|
+
? command.execute(executeData)
|
|
327
|
+
: command.execute(executeData))
|
|
328
|
+
.then(() => this.emit('recipleCommandExecute', executeData))
|
|
329
|
+
.catch(async (err) => await this._haltCommand(command, { executeData: executeData, reason: CommandHaltReason.Error, error: err })
|
|
330
|
+
? this._commandExecuteError(err, executeData)
|
|
331
|
+
: void 0);
|
|
332
|
+
return executeData;
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
if (!await this._haltCommand(command, { executeData: executeData, reason: CommandHaltReason.Error, error: err })) {
|
|
336
|
+
this._commandExecuteError(err, executeData);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Error message when a command fails to execute
|
|
342
|
+
* @param err Received error
|
|
343
|
+
* @param command Slash/Message command execute data
|
|
344
|
+
*/
|
|
345
|
+
async _commandExecuteError(err, command) {
|
|
346
|
+
if (this.isClientLogsEnabled()) {
|
|
347
|
+
this.logger.error(`An error occured executing ${command.builder.type == CommandBuilderType.MessageCommand ? 'message' : 'slash'} command "${command.builder.name}"`);
|
|
348
|
+
this.logger.error(err);
|
|
349
|
+
}
|
|
350
|
+
if (!err || !command)
|
|
351
|
+
return;
|
|
352
|
+
if (SlashCommandBuilder.isSlashCommandExecuteData(command)) {
|
|
353
|
+
if (!this.config.commands.slashCommand.replyOnError)
|
|
354
|
+
return;
|
|
355
|
+
await command.interaction.followUp(this.getConfigMessage('error', 'An error occurred.')).catch(er => this._replyError(er));
|
|
356
|
+
}
|
|
357
|
+
else if (MessageCommandBuilder.isMessageCommandExecuteData(command)) {
|
|
358
|
+
if (!this.config.commands.messageCommand.replyOnError)
|
|
359
|
+
return;
|
|
360
|
+
await command.message.reply(this.getConfigMessage('error', 'An error occurred.')).catch(er => this._replyError(er));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs.js';
|
|
2
|
+
import { isSupportedVersion, version } from '../version.js';
|
|
3
|
+
import { input, replaceAll } from 'fallout-utility.js';
|
|
4
|
+
import { cwd, token as __token } from '../flags.js';
|
|
5
|
+
import path from 'path.js';
|
|
6
|
+
import yaml from 'yaml.js';
|
|
7
|
+
/**
|
|
8
|
+
* Create/parse reciple config
|
|
9
|
+
*/
|
|
10
|
+
export class RecipleConfig {
|
|
11
|
+
config = RecipleConfig.getDefaultConfig();
|
|
12
|
+
configPath = path.join(cwd, 'reciple.yml');
|
|
13
|
+
static defaultConfigPath = path.join(__dirname, '../../../resource/reciple.yml');
|
|
14
|
+
/**
|
|
15
|
+
* @param configPath Path to config
|
|
16
|
+
*/
|
|
17
|
+
constructor(configPath) {
|
|
18
|
+
if (!configPath)
|
|
19
|
+
throw new Error('Config path is not defined');
|
|
20
|
+
this.configPath = configPath;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse the config file
|
|
24
|
+
*/
|
|
25
|
+
parseConfig() {
|
|
26
|
+
if (!existsSync(this.configPath)) {
|
|
27
|
+
const defaultConfigPath = RecipleConfig.defaultConfigPath;
|
|
28
|
+
if (!existsSync(defaultConfigPath))
|
|
29
|
+
throw new Error('Default Config file not found. Please reinstall Reciple.');
|
|
30
|
+
const defaultConfig = replaceAll(readFileSync(defaultConfigPath, 'utf-8'), 'VERSION', version);
|
|
31
|
+
writeFileSync(this.configPath, defaultConfig, 'utf-8');
|
|
32
|
+
if (!existsSync(this.configPath))
|
|
33
|
+
throw new Error('Failed to create config file.');
|
|
34
|
+
this.config = yaml.parse(defaultConfig);
|
|
35
|
+
if (this.config && this.config.token === 'TOKEN') {
|
|
36
|
+
this.config.token = this._askToken() || this.config.token;
|
|
37
|
+
writeFileSync(this.configPath, replaceAll(defaultConfig, ' TOKEN', ` ${this.config.token}`), 'utf-8');
|
|
38
|
+
}
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
if (!existsSync(this.configPath))
|
|
42
|
+
throw new Error('Failed to read config file.');
|
|
43
|
+
const config = readFileSync(this.configPath, 'utf-8');
|
|
44
|
+
this.config = yaml.parse(config);
|
|
45
|
+
if (!this._isSupportedConfig())
|
|
46
|
+
throw new Error('Unsupported config version. Your config version: ' + (this.config?.version || 'No version specified.') + ', Reciple version: ' + version);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns the parsed config file
|
|
51
|
+
*/
|
|
52
|
+
getConfig() {
|
|
53
|
+
if (!this.config)
|
|
54
|
+
throw new Error('Config is not parsed.');
|
|
55
|
+
this.config.token = this.parseToken() || 'TOKEN';
|
|
56
|
+
return this.config;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse token from config
|
|
60
|
+
* @param askIfNull Ask for token if the token is null/undefined
|
|
61
|
+
*/
|
|
62
|
+
parseToken(askIfNull = true) {
|
|
63
|
+
let token = __token || this.config?.token || null;
|
|
64
|
+
if (!token)
|
|
65
|
+
return token || (askIfNull ? this._askToken() : null);
|
|
66
|
+
const envToken = token.toString().split(':');
|
|
67
|
+
if (envToken.length === 2 && envToken[0].toLocaleLowerCase() === 'env' && envToken[1]) {
|
|
68
|
+
token = process.env[envToken[1]] || null;
|
|
69
|
+
}
|
|
70
|
+
return token || (askIfNull ? this._askToken() : null);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if the config version is supported
|
|
74
|
+
*/
|
|
75
|
+
_isSupportedConfig() {
|
|
76
|
+
return isSupportedVersion(this.config?.version || '0.0.0', version);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Ask for a token
|
|
80
|
+
*/
|
|
81
|
+
_askToken() {
|
|
82
|
+
return __token || input({ text: 'Bot Token >>> ', echo: '*', repeatIfEmpty: true, sigint: true }) || null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get default config
|
|
86
|
+
*/
|
|
87
|
+
static getDefaultConfig() {
|
|
88
|
+
if (!existsSync(this.defaultConfigPath))
|
|
89
|
+
throw new Error("Default config file does not exists.");
|
|
90
|
+
return yaml.parse(readFileSync(this.defaultConfigPath, 'utf-8'));
|
|
91
|
+
}
|
|
92
|
+
}
|