forked from Galactic/galactic-bot
grant/revoke basework, component command, guild wrapper rework (broke settings)
This commit is contained in:
parent
b9cfd331c5
commit
a630bf37ed
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ node_modules
|
||||
yarn-error.log
|
||||
.eslintrc.json
|
||||
logs
|
||||
permissionExample.json
|
@ -25,11 +25,11 @@ class LocaleLoader {
|
||||
const _directory = path.join(process.cwd(), 'language/languages');
|
||||
const directories = fs.readdirSync(_directory); //locale directories, ex. en_us, fi
|
||||
|
||||
for (const directory of directories) await this.loadLanguage(directory);
|
||||
for (const directory of directories) this.loadLanguage(directory);
|
||||
|
||||
}
|
||||
|
||||
async loadLanguage(language) {
|
||||
loadLanguage(language) {
|
||||
|
||||
this.client.logger.info(`Loading locale ${chalk.bold(language)}`);
|
||||
const directory = path.join(process.cwd(), `language/languages/${language}`);
|
||||
@ -76,7 +76,7 @@ class LocaleLoader {
|
||||
}
|
||||
|
||||
for(const [ key, value ] of Object.entries(parsed)) {
|
||||
parsed[key] = value.replace(/^(\r)|(\r){1,}$/g, '')
|
||||
parsed[key] = value.replace(/^(\r)|(\r){1,}$/g, '');
|
||||
}
|
||||
|
||||
return parsed;
|
||||
|
20
language/languages/en_us/commands/en_us_administrator.lang
Normal file
20
language/languages/en_us/commands/en_us_administrator.lang
Normal file
@ -0,0 +1,20 @@
|
||||
//Grant Command
|
||||
|
||||
[C_GRANT_DESCRIPTION]
|
||||
Grant roles or users permissions for commands or modules.
|
||||
|
||||
[C_GRANT_RESOLVEERROR]
|
||||
Unable to find any roles or members, view `{prefix}cmd grant` for more help.
|
||||
|
||||
[C_GRANT_MISSINGPERMPARAM]
|
||||
You must provide permissions to grant, view `{prefix}cmd grant` for more help.
|
||||
|
||||
//Revoke Command
|
||||
|
||||
[C_REVOKE_DESCRIPTION]
|
||||
Revoke permissions granted to roles or users.
|
||||
|
||||
//Permissions Command
|
||||
|
||||
[C_PERMISSIONS_DESCRIPTION]
|
||||
View permissions granted to roles or users.
|
5
language/languages/en_us/commands/en_us_developer.lang
Normal file
5
language/languages/en_us/commands/en_us_developer.lang
Normal file
@ -0,0 +1,5 @@
|
||||
[C_EVALUATE_DESCRIPTION]
|
||||
Evaluates javascript code.
|
||||
|
||||
[C_RELOAD_DESCRIPTION]
|
||||
Reloads components and language files.
|
@ -1,3 +0,0 @@
|
||||
[C_HELP_404]
|
||||
**ERROR:**
|
||||
`{component}` was not found!
|
@ -1,4 +1,9 @@
|
||||
[C_HELP_USAGE]
|
||||
//Help Command
|
||||
|
||||
[C_HELP_DESCRIPTION]
|
||||
Shows helpful information for commands, settings, and moderation.
|
||||
|
||||
[C_HELP]
|
||||
**__HELP MENU__**
|
||||
For a list of all available commands, use `{prefix}commands [ group ]`.
|
||||
|
||||
@ -12,6 +17,8 @@ The bot splits arguments by space unless specified otherwise. To pass an argumen
|
||||
**❯ Documentation notation**
|
||||
**Optional** arguments are denoted by being encapsulated in brackets `[ ]` - means that the command will run either with default values or show usage prompt.
|
||||
**Required** arguments are denoted by less and greater than `< >` - means that the command will not run and return an error.
|
||||
**Infinite** arguments (ones you can list several) are denoted by `..` after the argument. Ex `< argument.. >` - means you can pass more than one argument.
|
||||
**Alternatives** are denoted by being separated by a `|`.
|
||||
|
||||
**❯ Moderation**
|
||||
For help with moderation, see `{prefix}help modhelp`.
|
||||
@ -35,4 +42,23 @@ __**{component} HELP**__
|
||||
|
||||
[C_HELP_HELP]
|
||||
Shows information about the bot and component usage.
|
||||
To show help for commands `{prefix}help [ command ]`
|
||||
To show help for commands `{prefix}help [command]`
|
||||
|
||||
//Commands Command
|
||||
|
||||
[C_COMMANDS_DESCRIPTION]
|
||||
Displays all commands and help.
|
||||
|
||||
[C_COMMANDS_TITLE]
|
||||
Available commands
|
||||
|
||||
[C_COMMANDS_TEMPLATE]
|
||||
Displaying commands for module `{mod}`.
|
||||
|
||||
{text}
|
||||
|
||||
[C_COMMANDS]
|
||||
__Commands that are struck through are disabled.__
|
||||
|
||||
[C_COMMANDS_FOOTER]
|
||||
To search by specific module, use {prefix}commands [ module ]
|
@ -3,8 +3,14 @@
|
||||
[C_PING_RESPONSE]
|
||||
Pong!
|
||||
|
||||
[C_PING_DESCRIPTION]
|
||||
Shows the millisecond delay between the bot and the discord server.
|
||||
|
||||
//Settings Command
|
||||
|
||||
[C_SETTINGS_DESCRIPTION]
|
||||
Configure your guild and user settings.
|
||||
|
||||
[C_SETTINGS_ADMINISTRATORERROR]
|
||||
You must have the `ADMINISTRATOR` permission to reset the {type} settings.
|
||||
|
||||
@ -43,6 +49,9 @@ That setting does not exist!
|
||||
|
||||
//User Command
|
||||
|
||||
[C_USER_DESCRIPTION]
|
||||
Search for users or view user information.
|
||||
|
||||
[C_USER]
|
||||
**Nickname:** {nickname}
|
||||
**User:** <@{id}>
|
||||
|
@ -11,5 +11,11 @@ switch({component}) {
|
||||
break;
|
||||
}
|
||||
|
||||
[EXAMPLES]
|
||||
Example usage
|
||||
[GENERAL_EXAMPLES]
|
||||
Example Usage
|
||||
|
||||
[GENERAL_ALIASES]
|
||||
Aliases
|
||||
|
||||
[GENERAL_ARGUMENTS]
|
||||
Arguments
|
@ -15,3 +15,5 @@ Information
|
||||
|
||||
[M_MUSIC_NAME]
|
||||
Music
|
||||
|
||||
[
|
||||
|
@ -19,7 +19,7 @@ class DiscordWebhook extends Transport {
|
||||
|
||||
log(info, callback) {
|
||||
setImmediate(() => {
|
||||
this.emit('logged', info);
|
||||
this.emit('logged', info);
|
||||
});
|
||||
|
||||
const message = info.message.replace(regex, '')
|
||||
|
@ -10,7 +10,7 @@ const Logger = require('./Logger.js');
|
||||
const TransactionHandler = require('./TransactionHandler.js');
|
||||
const LocaleLoader = require('../../language/LocaleLoader.js');
|
||||
|
||||
const { Guild, User, Message } = require('../../structure/extensions/'); //eslint-disable-line
|
||||
const { Guild, GuildMember, User, Message } = require('../../structure/extensions/'); //eslint-disable-line no-unused-vars
|
||||
const { Command, Observer, Inhibitor, Setting } = require('../../structure/interfaces/');
|
||||
|
||||
class DiscordClient extends Client {
|
||||
@ -47,7 +47,7 @@ class DiscordClient extends Client {
|
||||
|
||||
await super.login(this._options.bot.token);
|
||||
|
||||
await this.localeLoader.loadLanguages();
|
||||
this.localeLoader.loadLanguages();
|
||||
|
||||
await this.registry.loadComponents('components/inhibitors/', Inhibitor);
|
||||
await this.registry.loadComponents('components/commands/', Command);
|
||||
@ -69,7 +69,7 @@ class DiscordClient extends Client {
|
||||
def = {
|
||||
...def,
|
||||
...setting.default
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
this._defaultConfig = def;
|
||||
|
@ -24,12 +24,14 @@ class Registry {
|
||||
const func = require(path);
|
||||
if(typeof func !== 'function') {
|
||||
this.client.logger.warn("Attempted to index an invalid function as a component.");
|
||||
delete require.cache[path];
|
||||
continue;
|
||||
}
|
||||
|
||||
const component = new func(this.client); //Instantiates the component class.
|
||||
if(classToHandle && !(component instanceof classToHandle)) {
|
||||
this.client.logger.warn("Attempted to load an invalid class.");
|
||||
delete require.cache[path];
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -44,6 +46,7 @@ class Registry {
|
||||
async loadComponent(component, directory) {
|
||||
|
||||
if(!(component instanceof Component)) {
|
||||
delete require.cache[directory];
|
||||
this.client.logger.warn("Attempted to load an invalid component.");
|
||||
return null;
|
||||
}
|
||||
@ -69,7 +72,10 @@ class Registry {
|
||||
}
|
||||
|
||||
async unloadComponent(component) {
|
||||
this.components.delete(component.id);
|
||||
if(component.module) {
|
||||
component.module.components.delete(component.resolveable);
|
||||
}
|
||||
this.components.delete(component.resolveable);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -161,13 +161,13 @@ class Resolver {
|
||||
if(/<@!?([0-9]{17,21})>/.test(resolveable)) {
|
||||
|
||||
let id = resolveable.match(/<@!?([0-9]{17,21})>/)[1];
|
||||
let member = await members.fetch(id).catch(err => { if(err.code === 10007) return false; else { console.warn(err); return false; } });
|
||||
let member = await members.fetch(id).catch(err => { if(err.code === 10007) return false; else { this.client.logger.warn(err); return false; } });
|
||||
if(member) resolved.push(member);
|
||||
|
||||
} else if(/(id\:)?([0-9]{17,21})/.test(resolveable)) {
|
||||
|
||||
let id = resolveable.match(/(id\:)?([0-9]{17,21})/)[2];
|
||||
let member = await members.fetch(id).catch(err => { if(err.code === 10007) return false; else { console.warn(err); return false; } });
|
||||
let member = await members.fetch(id).catch(err => { if(err.code === 10007) return false; else { this.client.logger.warn(err); return false; } });
|
||||
if(member) resolved.push(member);
|
||||
|
||||
} else if(/^\@?([\S\s]{1,32})\#([0-9]{4})/.test(resolveable)) {
|
||||
@ -175,21 +175,21 @@ class Resolver {
|
||||
let m = resolveable.match(/^\@?([\S\s]{1,32})\#([0-9]{4})/);
|
||||
let username = m[1].toLowerCase();
|
||||
let discrim = m[2].toLowerCase();
|
||||
let [ member ] = members.cache.filter(m => {
|
||||
let member = members.cache.filter(m => {
|
||||
return m.user.username.toLowerCase() === username && m.user.discriminator === discrim;
|
||||
}).first(1);
|
||||
}).first();
|
||||
if(member) resolved.push(member);
|
||||
|
||||
} else if(/^\@?([\S\s]{1,32})/.test(resolveable) && guild && !strict) {
|
||||
|
||||
let nickname = resolveable.match(/^\@?([\S\s]{1,32})/)[0].toLowerCase();
|
||||
let [ member ] = members.cache.filter((m) => {
|
||||
let member = members.cache.filter((m) => {
|
||||
return (m && m.user) &&
|
||||
((!m.nickname ? false : m.nickname.toLowerCase() == nickname ) ||
|
||||
(!m.nickname ? false : m.nickname.toLowerCase().includes(nickname)) ||
|
||||
m.user.username.toLowerCase().includes(nickname) ||
|
||||
m.user.username.toLowerCase() == nickname);
|
||||
}).first(1);
|
||||
}).first();
|
||||
if(member) resolved.push(member);
|
||||
|
||||
}
|
||||
@ -200,6 +200,13 @@ class Resolver {
|
||||
|
||||
}
|
||||
|
||||
async resolveMember(resolveable, guild, strict) {
|
||||
|
||||
let result = await this.resolveMembers([ resolveable ], guild, strict);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve multiple channels
|
||||
*
|
||||
@ -227,12 +234,12 @@ class Resolver {
|
||||
let name = /^\#?([a-z0-9\-\_0]*)/i;
|
||||
let id = /^\<\#([0-9]*)\>/i;
|
||||
|
||||
if(name.test(resolveable)) {
|
||||
if (name.test(resolveable)) {
|
||||
|
||||
let match = resolveable.match(name);
|
||||
let ch = match[1].toLowerCase();
|
||||
|
||||
let channel = channels.cache.filter(c => {
|
||||
let [ channel ] = channels.cache.filter(c => {
|
||||
if(!strict) return c.name.toLowerCase().includes(ch);
|
||||
return c.name.toLowerCase() === ch;
|
||||
}).first(1);
|
||||
@ -287,7 +294,7 @@ class Resolver {
|
||||
|
||||
} else {
|
||||
|
||||
let role = roles.cache.filter(r => {
|
||||
let [ role ] = roles.cache.filter(r => {
|
||||
if(!strict) return r.name.toLowerCase().includes(resolveable.toLowerCase());
|
||||
return r.name.toLowerCase() === resolveable.toLowerCase();
|
||||
}).first(1);
|
||||
|
101
structure/client/components/commands/administrator/Grant.js
Normal file
101
structure/client/components/commands/administrator/Grant.js
Normal file
@ -0,0 +1,101 @@
|
||||
const { Command, Argument } = require('../../../../interfaces/');
|
||||
|
||||
class GrantCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'grant',
|
||||
module: 'administrator',
|
||||
usage: "<role|user> <permissions..>",
|
||||
examples: [
|
||||
"\"Server Moderators\" module:moderation",
|
||||
"@nolan#2887 command:kick"
|
||||
],
|
||||
memberPermissions: ['ADMINISTRATOR'],
|
||||
showUsage: true,
|
||||
guildOnly: true,
|
||||
arguments: [
|
||||
new Argument(client, {
|
||||
name: 'channel',
|
||||
aliases: [
|
||||
'channels'
|
||||
],
|
||||
type: 'CHANNEL',
|
||||
types: ['FLAG', 'VERBAL'],
|
||||
infinite: true
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { params, args }) {
|
||||
|
||||
const _permissions = await message.guild.permissions_();
|
||||
|
||||
const [ parse, ...perms ] = params;
|
||||
const resolveable = await this._parseResolveable(message, parse);
|
||||
if(perms.length === 0) {
|
||||
await message.respond(message.format('C_GRANT_MISSINGPERMPARAM'), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const permissions = this.client.registry.components.filter(c=>
|
||||
c.type === 'command'
|
||||
|| c.type === 'module'
|
||||
);
|
||||
|
||||
let parsed = [];
|
||||
let failed = [];
|
||||
for(const perm of perms) {
|
||||
const search = permissions.filter(filterInexact(perm)).first();
|
||||
if(!search) failed.push(perm);
|
||||
if(search.type === 'module') {
|
||||
for(const component of search.components.values()) {
|
||||
if(component.type === 'command') parsed.push(component.resolveable);
|
||||
//add check for grantable
|
||||
}
|
||||
} else {
|
||||
//add check for grantable
|
||||
parsed.push(search.resolveable);
|
||||
}
|
||||
}
|
||||
|
||||
let data = {};
|
||||
let existing = _permissions[resolveable.id];
|
||||
if(existing) {
|
||||
for(let perm of parsed) {
|
||||
if(existing.includes(perm)) failed.push(perm);
|
||||
else existing.push(perm);
|
||||
}
|
||||
} else {
|
||||
existing = parsed;
|
||||
}
|
||||
data = existing;
|
||||
|
||||
|
||||
}
|
||||
|
||||
async _parseResolveable(message, resolveable) {
|
||||
let parsed = await this.client.resolver.resolveRoles(resolveable, message.guild);
|
||||
if(!parsed) {
|
||||
parsed = await this.client.resolver.resolveMembers(resolveable, message.guild);
|
||||
if(!parsed) {
|
||||
await message.respond(message.format('C_GRANT_RESOLVEERROR'), { emoji: 'failure' });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return parsed[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = GrantCommand;
|
||||
|
||||
const filterInexact = (search) => {
|
||||
return comp => comp.id.toLowerCase().includes(search) ||
|
||||
comp.resolveable.toLowerCase().includes(search) ||
|
||||
(comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase().includes(search)) ||
|
||||
comp.aliases.some(ali => ali.toLowerCase().includes(search))));
|
||||
};
|
37
structure/client/components/commands/administrator/Revoke.js
Normal file
37
structure/client/components/commands/administrator/Revoke.js
Normal file
@ -0,0 +1,37 @@
|
||||
const { Command, Argument } = require('../../../../interfaces/');
|
||||
|
||||
class RevokeCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'revoke',
|
||||
module: 'administrator',
|
||||
usage: "<role|user> <permissions..>",
|
||||
examples: [
|
||||
"\"Server Moderators\" module:moderation",
|
||||
"@nolan#2887 command:kick"
|
||||
],
|
||||
memberPermissions: ['ADMINISTRATOR'],
|
||||
showUsage: true,
|
||||
guildOnly: true,
|
||||
arguments: [
|
||||
new Argument(client, {
|
||||
name: 'channel',
|
||||
type: 'CHANNEL',
|
||||
types: ['FLAG', 'VERBAL'],
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { params, args }) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = RevokeCommand;
|
158
structure/client/components/commands/developer/Component.js
Normal file
158
structure/client/components/commands/developer/Component.js
Normal file
@ -0,0 +1,158 @@
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
class ComponentCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'component',
|
||||
module: 'developer',
|
||||
restricted: true,
|
||||
aliases: [
|
||||
'c',
|
||||
'comp'
|
||||
],
|
||||
usage: '[component..]',
|
||||
arguments: [
|
||||
// new Argument(client, {
|
||||
// name: 'reload',
|
||||
// type: 'STRING',
|
||||
// types: ['FLAG'],
|
||||
// aliases: [ 'r' ],
|
||||
// description: "Reloads the language library",
|
||||
// default: 'all'
|
||||
// })
|
||||
],
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { params }) {
|
||||
|
||||
//<load|unload|reload|disable|enable> <component>
|
||||
const method = params.shift().toLowerCase();
|
||||
let response;
|
||||
|
||||
if (method === 'reload')
|
||||
response = this._handleReload(params);
|
||||
else if (method === 'enable')
|
||||
response = this._handleDisableEnable(params.shift().toLowerCase(), true);
|
||||
else if (method === 'disable')
|
||||
response = this._handleDisableEnable(params.shift().toLowerCase(), false);
|
||||
else if (method === 'load')
|
||||
response = this._handleLoadUnload(params.shift().toLowerCase(), true);
|
||||
else if (method === 'unload')
|
||||
response = this._handleLoadUnload(params.shift().toLowerCase(), false);
|
||||
else return await message.respond('Invalid method. Can only be `reload`, `enable`, `disable`, `load`, `unload`', 'failure');
|
||||
|
||||
return await message.respond(response.msg, { emoji: response.error ? 'failure' : 'success' });
|
||||
|
||||
}
|
||||
|
||||
_handleReload(params) {
|
||||
|
||||
const name = params.length ? params.shift().toLowerCase() : 'all'; //ex. language
|
||||
const value = params.length ? params.shift().toLowerCase() : 'all'; //ex. en_us --> combined: -component reload language en_us
|
||||
|
||||
if (name === 'language' || name === 'lang' || name === 'l') {
|
||||
|
||||
if (value === 'all') {
|
||||
this.client.localeLoader.loadLanguages();
|
||||
return { msg: 'Reloaded all languages' };
|
||||
} else {
|
||||
try {
|
||||
this.client.localeLoader.loadLanguage(value);
|
||||
return { msg: `Reloaded locale \`${value}\`` };
|
||||
} catch (err) {
|
||||
return { error: true, msg: err.message };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (name === 'all') {
|
||||
|
||||
const errors = [];
|
||||
const components = this.client.registry.components;
|
||||
for (let component of components.values()) {
|
||||
const result = component.reload();
|
||||
if (result.error) errors.push(`Component ${component.id} errored while reloading with code \`${result.code}\``);
|
||||
}
|
||||
if (errors.length) return { error: true, msg: `The following errors occurred during reload:\n${errors.join('\n')}` };
|
||||
return { msg: `Successfully reloaded all components` };
|
||||
|
||||
} else {
|
||||
|
||||
const component = this.client.registry.components.get(name);
|
||||
if (!component) return { error: true, msg: `Component ${name} doesn't exist.` };
|
||||
const result = component.reload();
|
||||
if (result.error) return { error: true, msg: `Component ${name} errored while reloading with code \`${result.code}\`` };
|
||||
else return { msg: `Successfully reloaded ${name}` };
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_handleDisableEnable(name, enable) {
|
||||
|
||||
const component = this.client.registry.components.get(name);
|
||||
let result;
|
||||
if (!component) return { error: true, msg: `Component ${name} doesn't exist.` };
|
||||
if (enable) result = component.enable();
|
||||
else result = component.disable();
|
||||
|
||||
if (result.error) return { error: true, msg: `Cannot ${enable ? 'enable' : 'disable'} ${name} due to ${result.code}` };
|
||||
else return { msg: `Successfully ${enable ? 'enabled' : 'disabled'} component ${name}` };
|
||||
|
||||
}
|
||||
|
||||
_handleLoadUnload(name, load) {
|
||||
|
||||
let result;
|
||||
if (load) {
|
||||
const directory = path.join(process.cwd(), 'structure/client/components', name);
|
||||
try {
|
||||
fs.accessSync(directory);
|
||||
} catch(err) {
|
||||
return { error: true, msg: `\`${name}\` is an invalid path!` };
|
||||
}
|
||||
// components/commands/utility/Ping.js
|
||||
const func = require(directory); //directory
|
||||
if (typeof func !== 'function') {
|
||||
delete require.cache[directory];
|
||||
return { error: true, msg: 'Attempted to index an invalid function as a component.' };
|
||||
}
|
||||
const component = new func(this.client);
|
||||
result = this.client.registry.loadComponent(component, directory);
|
||||
if (!result) return { error: true, msg: `Failed to load component ${name}, see console.` };
|
||||
return { msg: `Successfully loaded component: ${component.resolveable}` };
|
||||
|
||||
} else {
|
||||
const component = this.client.registry.components.filter(filterInexact(name)).first();
|
||||
if (!component) return { error: true, msg: `Component ${name} doesn't exist.` };
|
||||
result = component.unload();
|
||||
}
|
||||
|
||||
if (result.error) return { error: true, msg: `Cannot ${load ? 'load' : 'unload'} ${name} due to ${result.code}` };
|
||||
else return { msg: `Successfully ${load ? 'loaded' : 'unloaded'} component ${name}` };
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ComponentCommand;
|
||||
|
||||
const filterInexact = (search) => {
|
||||
return comp => comp.id.toLowerCase().includes(search) ||
|
||||
comp.resolveable.toLowerCase().includes(search) ||
|
||||
(comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase().includes(search)) ||
|
||||
comp.aliases.some(ali => ali.toLowerCase().includes(search))));
|
||||
};
|
@ -18,8 +18,8 @@ class Evaluate extends Command {
|
||||
'eval',
|
||||
'e'
|
||||
],
|
||||
usage: '<code>',
|
||||
restricted: true,
|
||||
description: "Evaluates javascript code.",
|
||||
arguments: [
|
||||
new Argument(client, {
|
||||
name: 'log',
|
||||
@ -33,12 +33,12 @@ class Evaluate extends Command {
|
||||
types: ['FLAG'],
|
||||
description: "Hides the output from the channel."
|
||||
})
|
||||
]
|
||||
],
|
||||
showUsage: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
async execute(message, { params, args }) {
|
||||
|
||||
params = params.join(' ');
|
||||
|
@ -1,52 +0,0 @@
|
||||
const { Command, Argument } = require('../../../../interfaces/');
|
||||
|
||||
class ReloadCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'reload',
|
||||
module: 'developer',
|
||||
description: 'Reloads components and locales.',
|
||||
restricted: true,
|
||||
aliases: ['r'],
|
||||
arguments: [
|
||||
new Argument(client, {
|
||||
name: 'language',
|
||||
type: 'STRING',
|
||||
types: ['FLAG'],
|
||||
aliases: [ 'lang' ],
|
||||
description: "Reloads the language library",
|
||||
default: 'all'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { args }) {
|
||||
|
||||
if (args.language) {
|
||||
if (args.language.value === 'all') {
|
||||
await this.client.localeLoader.loadLanguages();
|
||||
return message.respond('Reloaded all languages');
|
||||
} else {
|
||||
try {
|
||||
await this.client.localeLoader.loadLanguage(args.language.value);
|
||||
return message.respond(`Reloaded locale \`${args.language.value}\``);
|
||||
} catch (err) {
|
||||
return message.respond(err.message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ReloadCommand;
|
110
structure/client/components/commands/information/Commands.js
Normal file
110
structure/client/components/commands/information/Commands.js
Normal file
@ -0,0 +1,110 @@
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
|
||||
class CommandsCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'commands',
|
||||
module: 'information',
|
||||
aliases: [
|
||||
'cmd',
|
||||
'cmds',
|
||||
'command'
|
||||
],
|
||||
usage: '[module]',
|
||||
arguments: [
|
||||
// new Argument(client, {
|
||||
// name: 'user',
|
||||
// type: 'BOOLEAN',
|
||||
// types: ['VERBAL', 'FLAG'],
|
||||
// default: true
|
||||
// }),
|
||||
]
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { params }) {
|
||||
|
||||
if (!params.length) // list all commands
|
||||
return this._listCommands(message);
|
||||
|
||||
params = params.join(' ');
|
||||
|
||||
const [ mod ] = this.client.resolver.components(params, 'module', false);
|
||||
if(!mod) {
|
||||
const [ command ] = this.client.resolver.components(params, 'command', false);
|
||||
if (!command) return message.format('C_COMMAND_INVALID');
|
||||
return await message._showUsage(command);
|
||||
}
|
||||
//list module's commands
|
||||
|
||||
const commands = mod.components.filter(c=>c.type === 'command');
|
||||
let text = '';
|
||||
|
||||
for(let command of commands.values()) {
|
||||
text += command.disabled ? `~~${command.name}~~\n` : `${command.name}`; //TODO: Denote disabled commands somehow
|
||||
}
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name: message.format('C_COMMANDS_TITLE'),
|
||||
icon_url: this.client.user.avatarURL()
|
||||
},
|
||||
description: message.format('C_COMMANDS') + '\n' + message.format('C_COMMANDS_TEMPLATE', { mod: mod.name, text }),
|
||||
footer: {
|
||||
text: message.format('C_COMMANDS_FOOTER')
|
||||
}
|
||||
};
|
||||
|
||||
return message.embed(embed);
|
||||
|
||||
}
|
||||
|
||||
_listCommands(message) {
|
||||
|
||||
let fields = [];
|
||||
const sortedModules = this.client.registry.components
|
||||
.filter(c => c.type === 'module')
|
||||
.sort((a, b) => {
|
||||
const filter = c => c.type === 'command';
|
||||
return b.components.filter(filter) - a.components.filter(filter);
|
||||
});
|
||||
|
||||
for (const mod of sortedModules.values()) {
|
||||
let field = {
|
||||
name: mod.id,
|
||||
value: '',
|
||||
inline: true
|
||||
};
|
||||
|
||||
for (const command of mod.components.values()) {
|
||||
if (command.type !== 'command'
|
||||
|| (command.restricted && !this.client._options.bot.owners.includes(message.author.id))) continue;
|
||||
field.value += `${command.name}\n`;
|
||||
}
|
||||
if (field.value) fields.push(field);
|
||||
}
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name: message.format('C_COMMANDS_TITLE'),
|
||||
icon_url: this.client.user.avatarURL()
|
||||
},
|
||||
description: message.format('C_COMMANDS'),
|
||||
fields,
|
||||
footer: {
|
||||
text: message.format('C_COMMANDS_FOOTER')
|
||||
}
|
||||
};
|
||||
|
||||
return message.embed(embed);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = CommandsCommand;
|
@ -6,18 +6,20 @@ class HelpCommand extends Command {
|
||||
|
||||
super(client, {
|
||||
name: 'help',
|
||||
module: 'information',
|
||||
description: 'Get help!',
|
||||
showUsage: true
|
||||
module: 'information'
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { params }) {
|
||||
|
||||
if (!params.length)
|
||||
return await message.embed({
|
||||
description: message.format('C_HELP')
|
||||
});
|
||||
|
||||
const [ key ] = params;
|
||||
let [ result ] = this.client.resolver.components(key, 'command');
|
||||
if (!result) [ result ] = this.client.resolver.components(key, 'setting');
|
||||
|
@ -9,7 +9,6 @@ class PingCommand extends Command {
|
||||
super(client, {
|
||||
name: 'arguments',
|
||||
module: 'utility',
|
||||
description: "Tests the argument parsing of the command handler.",
|
||||
aliases: ['args', 'arg', 'argument'],
|
||||
arguments: [
|
||||
new Argument(client, {
|
||||
@ -32,7 +31,9 @@ class PingCommand extends Command {
|
||||
required: true,
|
||||
types: ['FLAG', 'VERBAL']
|
||||
})
|
||||
]
|
||||
],
|
||||
restricted: true,
|
||||
archivable: false
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
@ -28,9 +28,7 @@ class SettingCommand extends Command {
|
||||
default: true
|
||||
})
|
||||
],
|
||||
memberPermissions: ['ADMINISTRATOR'],
|
||||
showUsage: true,
|
||||
|
||||
showUsage: true
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
@ -67,17 +65,16 @@ class SettingCommand extends Command {
|
||||
|
||||
//Setting permission handling
|
||||
if(setting.clientPermissions.length > 0) {
|
||||
const missing = message.channel.permissionsFor(message.guild.me).missing(command.clientPermissions);
|
||||
const missing = message.channel.permissionsFor(message.guild.me).missing(setting.clientPermissions);
|
||||
if(missing.length > 0) {
|
||||
await message.respond(message.format('C_SETTINGS_CLIENTPERMISSIONERROR', { setting: setting.moduleResolveable, missing: missing.join(', ')}), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
} else if(setting.memberPermissions.length > 0) {
|
||||
const missing = message.channel.permissionsFor(message.member).missing(command.memberPermissions);
|
||||
if(missing.length > 0) {
|
||||
await message.respond(message.format('C_SETTINGS_MEMBERPERMISSIONERROR', { setting: setting.moduleResolveable, missing: missing.join(', ')}), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if(message.channel.permissionsFor(message.member).missing('ADMINISTRATOR').length > 0 && setting.resolve === 'GUILD') {
|
||||
await message.respond(message.format('C_SETTINGS_ADMINISTRATORERROR', { type: type.toLowerCase() }), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const response = await setting.handle(message, params.splice(1));
|
||||
@ -138,8 +135,8 @@ class SettingCommand extends Command {
|
||||
if(!bool) return message.respond(message.format('C_SETTINGS_RESETABORT'), { emoji: 'success' });
|
||||
|
||||
type === 'USER'
|
||||
? await message.author._deleteSettings()
|
||||
: await message.guild._deleteSettings();
|
||||
? await message.author._delete()
|
||||
: await message.guild._delete('guilds');
|
||||
|
||||
return message.respond(message.format('C_SETTINGS_RESETSUCCESS', { type: type.toLowerCase() }), { emoji: 'success' })
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
|
||||
class PingCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'test',
|
||||
module: 'utility',
|
||||
description: "Determines the ping of the bot.",
|
||||
arguments: [
|
||||
|
||||
]
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
|
||||
}
|
||||
|
||||
async execute(message) {
|
||||
|
||||
const time1 = new Date().getTime();
|
||||
let response = await this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'infractions', type: 'find', query: { case: 1 } } }).catch(err => { return err; });
|
||||
const time2 = new Date().getTime();
|
||||
console.log(time2-time1);
|
||||
|
||||
message.reply(JSON.stringify(response));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = PingCommand;
|
@ -10,30 +10,37 @@ const Guild = Structures.extend('Guild', (Guild) => {
|
||||
super(...args);
|
||||
|
||||
this._settings = null; //internal cache of current guild's settings; should ALWAYS stay the same as database.
|
||||
this._permissions = null; //internal cache, should always match database.
|
||||
|
||||
}
|
||||
|
||||
async settings() {
|
||||
|
||||
if(!this._settings) this._settings = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'guilds', type: 'findOne', query: { guildId: this.id } } });
|
||||
if(this._settings instanceof Promise) this._settings = await this._settings || null
|
||||
if(this._settings instanceof Promise) this._settings = await this._settings || null;
|
||||
if(!this._settings) {
|
||||
this._settings = this.client.defaultConfig;
|
||||
}
|
||||
|
||||
return this._settings;
|
||||
|
||||
}
|
||||
|
||||
/* Database Shortcuts */
|
||||
async permissions_() {
|
||||
if(!this._permissions) this._permissions = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'permissions', type: 'findOne', query: { guildId: this.id } } });
|
||||
if(this._permissions instanceof Promise) this._permissions = await this._permissions || null;
|
||||
if(!this._permissions) {
|
||||
this._permissions = {};
|
||||
}
|
||||
return this._permissions;
|
||||
}
|
||||
|
||||
async _deleteSettings() { //Delete whole entry - remove
|
||||
/* Settings Wrapper */
|
||||
|
||||
async _dbDelete(collection) { //Delete whole entry - remove
|
||||
try {
|
||||
await this.client.transactionHandler.send({
|
||||
provider: 'mongodb',
|
||||
request: {
|
||||
type: 'remove',
|
||||
collection: 'guilds',
|
||||
collection,
|
||||
query: {
|
||||
guildId: this.id
|
||||
}
|
||||
@ -46,21 +53,22 @@ const Guild = Structures.extend('Guild', (Guild) => {
|
||||
}
|
||||
}
|
||||
|
||||
async _updateSettings(data) { //Update property (upsert true) - updateOne
|
||||
async _dbUpdateOne(data, collection) { //Update property (upsert true) - updateOne
|
||||
var index = this.dbIndex(collection);
|
||||
try {
|
||||
await this.client.transactionHandler.send({
|
||||
provider: 'mongodb',
|
||||
request: {
|
||||
type: 'updateOne',
|
||||
collection: 'guilds',
|
||||
collection,
|
||||
query: {
|
||||
guildId: this.id
|
||||
},
|
||||
data
|
||||
}
|
||||
});
|
||||
this._settings = {
|
||||
...this._settings,
|
||||
index = {
|
||||
...index,
|
||||
...data
|
||||
};
|
||||
this._storageLog(`Database Update (guild:${this.id}).`);
|
||||
@ -69,18 +77,18 @@ const Guild = Structures.extend('Guild', (Guild) => {
|
||||
}
|
||||
}
|
||||
|
||||
async _removeSettings(value) { //Remove property
|
||||
if(this.client.defaultConfig[value]) {
|
||||
async _dbRemoveProperty(value, collection) { //Remove property
|
||||
const index = this.dbIndex(collection);
|
||||
if(collection === 'guild' && this.client.defaultConfig[value]) {
|
||||
await this._updateSettings(this.client.defaultConfig[value]);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.client.transactionHandler.send({
|
||||
provider: 'mongodb',
|
||||
request: {
|
||||
type: 'removeProperty',
|
||||
collection: 'guilds',
|
||||
collection,
|
||||
query: {
|
||||
guildId: this.id
|
||||
},
|
||||
@ -89,33 +97,23 @@ const Guild = Structures.extend('Guild', (Guild) => {
|
||||
]
|
||||
}
|
||||
});
|
||||
delete this._settings[value];
|
||||
delete index[value];
|
||||
this._storageLog(`Database Remove (guild:${this.id}).`);
|
||||
} catch(error) {
|
||||
this._storageError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
async _createSettings(data) {
|
||||
try {
|
||||
this.client.transactionHandler.send({
|
||||
provider: 'mongodb',
|
||||
request: {
|
||||
type: 'insertOne',
|
||||
collection: 'guilds',
|
||||
data: {
|
||||
guildId: this.id,
|
||||
...data
|
||||
}
|
||||
}
|
||||
});
|
||||
this._storageLog(`Database Create (guild:${this.id}).`);
|
||||
} catch(error) {
|
||||
this._storageError(error);
|
||||
}
|
||||
|
||||
dbIndex(collection) {
|
||||
return {
|
||||
'guilds': this._settings,
|
||||
'permissions': this._permissions
|
||||
}[collection];
|
||||
}
|
||||
*/
|
||||
|
||||
/* Permissions Wrapper */
|
||||
|
||||
|
||||
|
||||
/* Language Formatting */
|
||||
|
||||
|
26
structure/extensions/GuildMember.js
Normal file
26
structure/extensions/GuildMember.js
Normal file
@ -0,0 +1,26 @@
|
||||
const { Structures } = require('discord.js');
|
||||
|
||||
const GuildMember = Structures.extend('GuildMember', (GuildMember) => {
|
||||
|
||||
class ExtendedGuildMember extends GuildMember {
|
||||
|
||||
constructor(...args) {
|
||||
|
||||
super(...args);
|
||||
|
||||
this._cached = Date.now();
|
||||
|
||||
}
|
||||
|
||||
get timeSinceCached() {
|
||||
return Date.now()-this._cached;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return ExtendedGuildMember;
|
||||
|
||||
});
|
||||
|
||||
module.exports = GuildMember;
|
@ -4,6 +4,7 @@ const escapeRegex = require('escape-string-regexp');
|
||||
const emojis = require('../../util/emojis.json');
|
||||
|
||||
const { Util } = require('../../util/');
|
||||
const { stripIndents } = require('common-tags')
|
||||
|
||||
const Message = Structures.extend('Message', (Message) => {
|
||||
|
||||
@ -26,7 +27,7 @@ const Message = Structures.extend('Message', (Message) => {
|
||||
let language = this.author._settings.locale || 'en_us';
|
||||
if(this.guild && this.guild._settings.locale) language = this.guild._settings.locale;
|
||||
|
||||
parameters.prefix = this.guild ? this.guild.prefix : this.client._options.bot.prefix;
|
||||
parameters.prefix = this.guild?.prefix || this.client._options.bot.prefix;
|
||||
let template = this.client.localeLoader.template(language, index); //.languages[language][index];
|
||||
|
||||
if(!template) {
|
||||
@ -118,18 +119,46 @@ const Message = Structures.extend('Message', (Message) => {
|
||||
});
|
||||
}
|
||||
|
||||
async _showUsage() {
|
||||
//TODO: format this
|
||||
return await this.embed({
|
||||
title: `**${this.command.name.toUpperCase()} USAGE**`,
|
||||
description: this.format(`C_${this.command.name.toUpperCase()}_USAGE`),
|
||||
fields: [
|
||||
{
|
||||
name: this.format('EXAMPLES'),
|
||||
value: this.format(`C_${this.command.name.toUpperCase()}_EXAMPLES`)
|
||||
}
|
||||
]
|
||||
});
|
||||
async _showUsage(comp = null) {
|
||||
|
||||
const component = comp
|
||||
? comp
|
||||
: this.command;
|
||||
|
||||
const prefix = this.guild?.prefix
|
||||
|| this.client._options.bot.prefix;
|
||||
|
||||
let fields = [];
|
||||
if(component.examples.length > 0) {
|
||||
fields.push({
|
||||
name: `》${this.format('GENERAL_EXAMPLES')}`,
|
||||
value: component.examples.map(e=>`\`${prefix}${component.name} ${e}\``).join('\n')
|
||||
});
|
||||
}
|
||||
if(component.aliases.length > 0) {
|
||||
fields.push({
|
||||
name: `》${this.format('GENERAL_ALIASES')}`,
|
||||
value: component.aliases.map(a=>`\`${a}\``).join(', ')
|
||||
});
|
||||
}
|
||||
if(component.arguments.length > 0) {
|
||||
fields.push({
|
||||
name: `》${this.format('GENERAL_ARGUMENTS')}`,
|
||||
value: component.arguments.map(a=>`${a.name}: ${this.format(`A_${a.name.toUpperCase}_DESCRIPTION`)}`)
|
||||
});
|
||||
}
|
||||
|
||||
let embed = {
|
||||
author: {
|
||||
name: `${component.name}${component.module ? ` (${component.module.resolveable})` : ''}`,
|
||||
icon_url: this.client.user.avatarURL()
|
||||
},
|
||||
description: stripIndents`\`${prefix}${component.name}${component.usage ? ` ${component.usage}` : ''}\`
|
||||
${this.format(`C_${component.name.toUpperCase()}_DESCRIPTION`)}${component.guildOnly ? ' *(guild-only)*' : ''}`,
|
||||
fields
|
||||
};
|
||||
|
||||
return await this.embed(embed);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
Message: require('./Message.js'),
|
||||
Guild: require('./Guild.js'),
|
||||
GuildMember: require('./GuildMember.js'),
|
||||
User: require('./User.js')
|
||||
};
|
@ -19,14 +19,15 @@ class Command extends Component {
|
||||
this.aliases = opts.aliases || [];
|
||||
|
||||
this.description = `C_${opts.name}_DESCRIPTION`;
|
||||
this.examples = `C_${opts.name}_EXAMPLES`;
|
||||
this.usage = `C_${opts.name}_USAGE`;
|
||||
this.usage = opts.usage || '';
|
||||
this.examples = opts.examples || [];
|
||||
|
||||
this.restricted = Boolean(opts.restricted);
|
||||
this.archivable = opts.archivable === undefined ? false : Boolean(opts.archivable);
|
||||
this.guildOnly = Boolean(opts.guildOnly);
|
||||
this.arguments = opts.arguments || [];
|
||||
this.showUsage = Boolean(opts.showUsage);
|
||||
this.guildOnly = Boolean(opts.guildOnly);
|
||||
|
||||
this.archivable = opts.archivable === undefined ? false : Boolean(opts.archivable);
|
||||
this.arguments = opts.arguments || [];
|
||||
|
||||
this.clientPermissions = opts.clientPermissions || [];
|
||||
this.memberPermissions = opts.memberPermissions || [];
|
||||
|
@ -41,15 +41,8 @@ class Setting extends Component {
|
||||
}
|
||||
|
||||
async _parseArguments(params) {
|
||||
|
||||
const { parsedArguments, newArgs } = await this.commandHandler._parseArguments(this.arguments, params);
|
||||
return { parsedArguments, params: newArgs };
|
||||
|
||||
}
|
||||
|
||||
async _handleReset(message, params) {
|
||||
const response = await message.prompt(message.format('UHHH'), { error: 'warning' });
|
||||
|
||||
}
|
||||
|
||||
get commandHandler() {
|
||||
|
Loading…
Reference in New Issue
Block a user