command handler changes
This commit is contained in:
parent
943da83993
commit
a3faf1af13
@ -9,6 +9,7 @@
|
||||
"GUILD_MESSAGES"
|
||||
]
|
||||
},
|
||||
"invite": "https://discord.gg/49u6cHu",
|
||||
"shardOptions": {
|
||||
"totalShards": "auto"
|
||||
},
|
||||
|
@ -26,6 +26,7 @@
|
||||
"chalk": "^4.1.2",
|
||||
"discord.js": "^13.2.0-dev.1629115738.9a833b1",
|
||||
"dotenv": "^10.0.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"moment": "^2.29.1",
|
||||
"mongodb": "^3.5.9",
|
||||
|
12
src/Util.js
12
src/Util.js
@ -104,6 +104,16 @@ class Util {
|
||||
return ['.', '+', '*', '?', '\\[', '\\]', '^', '$', '(', ')', '{', '}', '|', '\\\\', '-'];
|
||||
}
|
||||
|
||||
static escapeRegex(string) {
|
||||
if(typeof string !== 'string') {
|
||||
throw new Error("Invalid type sent to escapeRegex.");
|
||||
}
|
||||
|
||||
return string
|
||||
.replace(/[|\\{}()[\]^$+*?.]/gu, '\\$&')
|
||||
.replace(/-/gu, '\\x2d');
|
||||
}
|
||||
|
||||
static duration(seconds) {
|
||||
const { plural } = this;
|
||||
let s = 0,
|
||||
@ -129,7 +139,7 @@ class Util {
|
||||
}
|
||||
|
||||
static get date() {
|
||||
return moment().format("YYYY-MM-DD hh:mm:ss");
|
||||
return moment().format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
Commands: require('./Commands.json'),
|
||||
Emojis: require('./Emojis.json')
|
||||
};
|
||||
}
|
@ -1,3 +1,30 @@
|
||||
[O_COMMANDHANDLER_COMMANDNOTSYNCED]
|
||||
It appears as if the command does not exist on the client.
|
||||
This is an issue that should be reported to a bot developer.
|
||||
|
||||
[O_COMMANDHANDLER_GUILDONLY]
|
||||
This command can only be run in servers.
|
||||
|
||||
[O_COMMANDHANDLER_TYPEINTEGER]
|
||||
The command option {option} requires an integer between `{min}` and `{max}`.
|
||||
|
||||
[O_COMMANDHANDLER_TYPEMEMBER]
|
||||
The command option {option} requires a server member.
|
||||
|
||||
[O_COMMANDHANDLER_TYPETEXT_CHANNEL]
|
||||
The command option {option} requires a text channel.
|
||||
|
||||
[O_COMMANDHANDLER_TYPEVOICE_CHANNEL]
|
||||
The command option {option} requires a voice channel.
|
||||
|
||||
[O_COMMANDHANDLER_TYPENUMBER]
|
||||
The command option {option} requires a number between `{min}` and `{max}`.
|
||||
|
||||
[O_COMMANDHANDLER_TYPEFLOAT]
|
||||
The command option {option} requires a float between `{min}` and `{max}`.
|
||||
|
||||
[O_COMMANDHANDLER_ERROR]
|
||||
An error occured while executing that command.
|
||||
It is recommended to contact a developer about this issue.
|
||||
|
||||
You can join the support server by clicking on the button below.
|
@ -28,13 +28,13 @@ class BaseClient extends EventEmitter {
|
||||
|
||||
async build() {
|
||||
|
||||
await this.shardingManager.spawn().catch((err) => {
|
||||
this.error(`Fatal error during shard spawning:\n${err.stack}`);
|
||||
await this.shardingManager.spawn().catch((error) => {
|
||||
this.logger.error(`Fatal error during shard spawning:\n${error.stack || error}`);
|
||||
// eslint-disable-next-line no-process-exit
|
||||
process.exit(); // Prevent a boot loop when shards die due to an error in the client
|
||||
});
|
||||
|
||||
const API = await import('./api/index.js').catch((err) => this.warn(`Error importing API files:\n${err.stack}`));
|
||||
const API = await import('./api/index.js').catch((error) => this.logger.warn(`Error importing API files:\n${error.stack || error}`));
|
||||
if (API) {
|
||||
this.info('Booting up API');
|
||||
const { default: APIManager } = API;
|
||||
@ -135,7 +135,7 @@ class BaseClient extends EventEmitter {
|
||||
|
||||
}
|
||||
|
||||
async getLiveGuild(shard, message) {
|
||||
async getLiveGuild(shard, message) { //eslint-disable-line no-unused-vars
|
||||
|
||||
// TODO: Figure out what exactly this should return, waiting for further client implementation
|
||||
// const result = await this.shardingManager.broadcastEval((client) => {
|
||||
@ -144,18 +144,6 @@ class BaseClient extends EventEmitter {
|
||||
|
||||
}
|
||||
|
||||
warn(message) {
|
||||
this.logger.write('warn', message);
|
||||
}
|
||||
|
||||
info(message) {
|
||||
this.logger.write('info', message);
|
||||
}
|
||||
|
||||
error(message) {
|
||||
this.logger.write('error', message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = BaseClient;
|
@ -4,6 +4,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const Util = require('../Util.js');
|
||||
const { runInThisContext } = require('vm');
|
||||
|
||||
const Constants = {
|
||||
Types: [
|
||||
@ -105,6 +106,26 @@ class Logger {
|
||||
return `${id}`.length === 1 ? `0${id}` : `${id}`;
|
||||
}
|
||||
|
||||
error(message) {
|
||||
this.write('error', message);
|
||||
}
|
||||
|
||||
warn(message) {
|
||||
this.write('warn', message);
|
||||
}
|
||||
|
||||
debug(message) {
|
||||
this.write('debug', message);
|
||||
}
|
||||
|
||||
info(message) {
|
||||
this.write('info', message);
|
||||
}
|
||||
|
||||
status(message) {
|
||||
this.write('status', message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Logger;
|
@ -24,16 +24,17 @@ class DiscordClient extends Client {
|
||||
|
||||
this.resolver = new Resolver(this);
|
||||
|
||||
this._activity = 0;
|
||||
this._options = options;
|
||||
this._built = false;
|
||||
|
||||
this.once('ready', () => {
|
||||
this._setActivity();
|
||||
// this.once('ready', () => {
|
||||
// this._setActivity();
|
||||
|
||||
setInterval(() => {
|
||||
this._setActivity();
|
||||
}, 1800000); // I think this is 30 minutes. I could be wrong.
|
||||
});
|
||||
// setInterval(() => {
|
||||
// this._setActivity();
|
||||
// }, 1800000); // I think this is 30 minutes. I could be wrong.
|
||||
// });
|
||||
|
||||
}
|
||||
|
||||
@ -87,7 +88,6 @@ class DiscordClient extends Client {
|
||||
return Boolean(this.shard.ids[0] === 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = DiscordClient;
|
||||
|
@ -23,6 +23,27 @@ class LocaleLoader {
|
||||
|
||||
}
|
||||
|
||||
format(language, index, parameters = {}, code = false) {
|
||||
|
||||
let string = this.languages[language][index];
|
||||
if(!string) return `< Missing Locale: ${language}.${index} >`;
|
||||
|
||||
for(const [ parameter, value ] of Object.entries(parameters)) {
|
||||
string = string.replace(new RegExp(`{${Util.escapeRegex(parameter.toLowerCase())}}`, 'giu'), value);
|
||||
}
|
||||
|
||||
if(code) {
|
||||
try {
|
||||
string = eval(string); //eslint-disable-line no-eval
|
||||
} catch(error) {
|
||||
this.client.logger.error(`Locale [${language}.${index}] failed to execute code.\n${error.stack || error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
|
||||
}
|
||||
|
||||
_loadLanguage(root, language) {
|
||||
|
||||
const directory = path.join(root, language);
|
||||
|
@ -0,0 +1,44 @@
|
||||
const { SlashCommand, CommandOption } = require("../../../interfaces");
|
||||
|
||||
class SettingsCommand extends SlashCommand {
|
||||
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'settings',
|
||||
description: "Invoke to display a settings menu.",
|
||||
module: 'administration',
|
||||
options: [
|
||||
new CommandOption({
|
||||
name: 'category',
|
||||
description: "Select a category to view settings for.",
|
||||
type: 'STRING',
|
||||
choices: [
|
||||
{
|
||||
name: 'Administration',
|
||||
value: 'administrator'
|
||||
},
|
||||
{
|
||||
name: 'Moderation',
|
||||
value: 'moderation'
|
||||
},
|
||||
{
|
||||
name: 'Utility',
|
||||
value: 'utility'
|
||||
}
|
||||
],
|
||||
required: true
|
||||
})
|
||||
],
|
||||
guildOnly: true
|
||||
});
|
||||
}
|
||||
|
||||
async execute(thing, options) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = SettingsCommand;
|
@ -33,10 +33,12 @@ class MuteCommand extends SlashCommand {
|
||||
});
|
||||
}
|
||||
|
||||
async execute(interaction) {
|
||||
async execute(thing, options) {
|
||||
|
||||
console.log(options);
|
||||
|
||||
// console.log(interaction, interaction.options);
|
||||
interaction.reply(this.resolveable);
|
||||
thing.reply({ content: this.resolveable });
|
||||
|
||||
}
|
||||
|
||||
|
@ -45,11 +45,29 @@ class CommandHandler extends Observer {
|
||||
const command = this._matchCommand(interaction.commandName);
|
||||
const thing = new Thing(this.client, command, interaction);
|
||||
|
||||
if(!command) return thing.reply({ locale: 'O_COMMANDHANDLER_COMMANDNOTSYNCED', emoji: 'failure', ephemeral: true });
|
||||
if(!command) return thing.reply({ content: thing.format('O_COMMANDHANDLER_COMMANDNOTSYNCED'), emoji: 'failure', ephemeral: true });
|
||||
|
||||
const response = await this._parseInteraction(thing);
|
||||
if(response.error) {
|
||||
|
||||
return thing.reply({
|
||||
content: thing.format(`O_COMMANDHANDLER_TYPE${response.option.type}`, { option: response.option.name, min: response.option.minimum, max: response.option.maximum }),
|
||||
emoji: 'failure',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
return this._executeCommand(thing, response.options);
|
||||
|
||||
}
|
||||
|
||||
async _executeCommand(thing, options) {
|
||||
|
||||
try {
|
||||
const resolved = thing.command.execute(thing, options);
|
||||
if(resolved instanceof Promise) await resolved;
|
||||
} catch(error) {
|
||||
this.client.logger.error(error.stack || error);
|
||||
this._generateError(thing);
|
||||
}
|
||||
|
||||
}
|
||||
@ -63,9 +81,12 @@ class CommandHandler extends Observer {
|
||||
}
|
||||
|
||||
let error = null;
|
||||
const options = {};
|
||||
|
||||
for(const option of interaction.options._hoistedOptions) {
|
||||
const matched = command.options.find((o) => o.name === option.name);
|
||||
const newOption = new CommandOption({ name: matched.name, type: matched.type, _rawValue: option.value });
|
||||
console.log('matched', matched)
|
||||
const newOption = new CommandOption({ name: matched.name, type: matched.type, minimum: matched.minimum, maximum: matched.maximum, _rawValue: option.value });
|
||||
|
||||
const parsed = await this._parseOption(thing, newOption);
|
||||
if(parsed.error) {
|
||||
@ -77,43 +98,43 @@ class CommandHandler extends Observer {
|
||||
}
|
||||
|
||||
newOption.value = parsed.value;
|
||||
thing.options.push(newOption);
|
||||
options[matched.name] = newOption;
|
||||
}
|
||||
|
||||
if(error) return error;
|
||||
return {
|
||||
error: false,
|
||||
//uhhh..
|
||||
}
|
||||
console.log(options);
|
||||
options
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
async _parseOption(thing, option) {
|
||||
|
||||
const types = {
|
||||
ROLES: (string) => {
|
||||
// ROLES: (string) => {
|
||||
|
||||
},
|
||||
MEMBERS: (string) => {
|
||||
// },
|
||||
// MEMBERS: (string) => {
|
||||
|
||||
},
|
||||
USERS: (string) => {
|
||||
// },
|
||||
// USERS: (string) => {
|
||||
|
||||
},
|
||||
CHANNELS: (string) => {
|
||||
// },
|
||||
// CHANNELS: (string) => {
|
||||
|
||||
},
|
||||
TEXT_CHANNELS: (string) => {
|
||||
// },
|
||||
// TEXT_CHANNELS: (string) => {
|
||||
|
||||
},
|
||||
VOICE_CHANNELS: (string) => {
|
||||
// },
|
||||
// VOICE_CHANNELS: (string) => {
|
||||
|
||||
},
|
||||
// },
|
||||
STRING: (string) => {
|
||||
return { error: false, value: string };
|
||||
},
|
||||
INTEGER: (integer) => {
|
||||
console.log(option);
|
||||
if(option.minimum !== undefined && integer < option.minimum) return { error: true };
|
||||
if(option.maximum !== undefined && integer > option.maximum) return { error: true };
|
||||
return { error: false, value: parseInt(integer) };
|
||||
@ -188,7 +209,26 @@ class CommandHandler extends Observer {
|
||||
return command || null;
|
||||
}
|
||||
|
||||
_generateError() {
|
||||
_generateError(thing) {
|
||||
|
||||
thing.reply({
|
||||
content: thing.format('O_COMMANDHANDLER_ERROR'),
|
||||
emoji: 'failure',
|
||||
ephemeral: true,
|
||||
components: [
|
||||
{
|
||||
type: 'ACTION_ROW',
|
||||
components: [
|
||||
{
|
||||
label: 'Support',
|
||||
type: 'BUTTON',
|
||||
style: 'LINK',
|
||||
url: this.client._options.discord.invite
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
18
src/structure/components/settings/moderation/MuteSetting.js
Normal file
18
src/structure/components/settings/moderation/MuteSetting.js
Normal file
@ -0,0 +1,18 @@
|
||||
const { Setting } = require('../../../interfaces/');
|
||||
|
||||
class MuteSetting extends Setting {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'mute',
|
||||
description: 'uhhhhh'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = MuteSetting;
|
@ -1,63 +0,0 @@
|
||||
const Constants = {
|
||||
ArgumentOptionTypes: { //https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-type
|
||||
"SUB_COMMAND": 1,
|
||||
"SUB_COMMAND_GROUP": 2,
|
||||
"STRING": 3,
|
||||
"INTEGER": 4,
|
||||
"BOOLEAN": 5,
|
||||
"USER": 6,
|
||||
"CHANNEL": 7,
|
||||
"ROLE": 8,
|
||||
"MENTIONABLE": 9,
|
||||
"NUMBER": 10
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
ApplicationCommandOption {
|
||||
type
|
||||
name
|
||||
description
|
||||
required
|
||||
choices
|
||||
options (only used for subcommands/groups)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
class Argument {
|
||||
|
||||
constructor(client, options = {}) {
|
||||
if(!options) return null;
|
||||
|
||||
this.client = client;
|
||||
|
||||
this.key = options.key?.toLowerCase();
|
||||
this.description = options.description;
|
||||
|
||||
this.aliases = options.aliases || null;
|
||||
|
||||
this.type = options.type || 'STRING';
|
||||
|
||||
this.aliases = options.aliases || [];
|
||||
this.types = options.types || ['FLAG', 'VERBAL'];
|
||||
this.choices = options.choices || [];
|
||||
|
||||
this.required = Boolean(options.required);
|
||||
this.infinite = Boolean(options.infinite);
|
||||
|
||||
this.parser = typeof options.parser === 'function' ? options.parser : null;
|
||||
|
||||
this.value = undefined; //Defined in the Command Handler.
|
||||
|
||||
}
|
||||
|
||||
get structure() {
|
||||
return {
|
||||
name: this.key,
|
||||
description: this.description,
|
||||
type:
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -35,8 +35,8 @@ class CommandOption {
|
||||
this.choices = options.choices || []; //Used for STRING/INTEGER/NUMBER types.
|
||||
this.options = options.options || []; //Used for SUB_COMMAND/SUB_COMMAND_GROUP types.
|
||||
|
||||
this.minimum = options.minimum || undefined; //Used for INTEGER/NUMBER/FLOAT types.
|
||||
this.maxiumum = options.maximum || undefined;
|
||||
this.minimum = typeof options.minimum === 'number' ? options.minimum : undefined; //Used for INTEGER/NUMBER/FLOAT types.
|
||||
this.maximum = typeof options.maximum === 'number' ? options.maximum : undefined;
|
||||
|
||||
this.value = undefined;
|
||||
|
||||
|
@ -18,18 +18,6 @@ class Observer extends Component {
|
||||
|
||||
}
|
||||
|
||||
execute() {
|
||||
return this._continue();
|
||||
}
|
||||
|
||||
_continue() {
|
||||
return { error: false, observer: this };
|
||||
}
|
||||
|
||||
_stop() {
|
||||
return { error: true, observer: this };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Observer;
|
24
src/structure/interfaces/Setting.js
Normal file
24
src/structure/interfaces/Setting.js
Normal file
@ -0,0 +1,24 @@
|
||||
const Component = require("./Component.js");
|
||||
|
||||
class Setting extends Component {
|
||||
|
||||
constructor(client, options = {}) {
|
||||
if(!options) return null;
|
||||
|
||||
super(client, {
|
||||
id: options.name,
|
||||
_type: 'setting',
|
||||
disabled: options.disabled,
|
||||
guarded: options.guarded
|
||||
});
|
||||
|
||||
this.name = options.name;
|
||||
this.module = options.module;
|
||||
|
||||
this.description = options.description || "";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Setting;
|
@ -19,11 +19,6 @@ class Thing {
|
||||
|
||||
async reply(options = {}) {
|
||||
|
||||
if(options.locale) {
|
||||
options.content = this.format(options.locale);
|
||||
// delete options.locale;
|
||||
}
|
||||
|
||||
if(options.emoji) {
|
||||
if(!Emojis[options.emoji]) this.client.logger.warn(`Invalid emoji provided to command ${this.command.resolveable}: "${options.emoji}"`);
|
||||
options.content = `${Emojis[options.emoji]} ${options.content}`;
|
||||
@ -34,10 +29,10 @@ class Thing {
|
||||
return this._pending;
|
||||
}
|
||||
|
||||
format(locale) {
|
||||
format(locale, parameters = {}, code = false) {
|
||||
const language = 'en_us'; //Default language.
|
||||
//TODO: Fetch guild/user settings and switch localization.
|
||||
return this.client.localeLoader.languages[language][locale];
|
||||
return this.client.localeLoader.format(language, locale, parameters, code);
|
||||
}
|
||||
|
||||
get guild() {
|
||||
|
@ -5,5 +5,6 @@ module.exports = {
|
||||
SlashCommand: require('./commands/SlashCommand.js'),
|
||||
Command: require('./commands/Command.js'),
|
||||
CommandOption: require('./CommandOption.js'),
|
||||
Thing: require('./Thing.js')
|
||||
Thing: require('./Thing.js'),
|
||||
Setting: require('./Setting.js')
|
||||
};
|
@ -679,6 +679,11 @@ escape-string-regexp@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
escape-string-regexp@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
|
||||
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
|
||||
|
||||
eslint-scope@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
|
Loading…
Reference in New Issue
Block a user