This commit is contained in:
Erik 2020-05-06 16:48:29 +03:00
commit 261c007645
21 changed files with 650 additions and 90 deletions

View File

@ -15,7 +15,8 @@ class LocaleLoader {
template(locale, index) { template(locale, index) {
return this.languages[locale] ? this.languages[locale][index] : `Locale ${locale} does not exist.` || `Entry ${locale}:${index} does not exist.`; // console.log(String.raw`${this.languages[locale][index]}`);
return (this.languages[locale] ? this.languages[locale][index] : `Locale ${locale} does not exist.`) || `Entry ${locale}:${index} does not exist.`;
} }
@ -55,6 +56,7 @@ class LocaleLoader {
let matched = null; let matched = null;
let text = ''; let text = '';
for(const line of lines) { for(const line of lines) {
if(line.startsWith('//')) continue;
const matches = line.match(/\[([_A-Z0-9]{1,})]/, 'gi'); const matches = line.match(/\[([_A-Z0-9]{1,})]/, 'gi');
if(matches) { if(matches) {
if(matched) { if(matched) {
@ -73,8 +75,8 @@ class LocaleLoader {
} }
} }
if(matched) { for(const [ key, value ] of Object.entries(parsed)) {
parsed[matched] = text; parsed[key] = value.replace(/^(\r)|(\r){1,}$/g, '')
} }
return parsed; return parsed;

View File

@ -1,21 +1,47 @@
[C_PING] //Ping Command
Pong! {number}
[C_MODE_TEST] [C_PING_RESPONSE]
switch('{mode}') { Pong!
case 'explicit':
'works1';
break;
case 'fuzzy':
'works2';
break;
}
[C_PING_HELP] //Settings Command
what?
[C_SETTING_RESET] [C_SETTINGS_ADMINISTRATORERROR]
Are you sure you want to reset __all__ of your guild settings to the default values? You must have the `ADMINISTRATOR` permission to reset the {type} settings.
[C_SETTINGS_CLIENTPERMISSIONERROR]
The setting **{setting}** requires __the bot__ to have permissions to use.
*Missing: {missing}*
[C_SETTINGS_MEMBERPERMISSIONERROR]
The setting **{setting}** requires __you__ to have permissions to use.
*Missing: {missing}*
[C_SETTINGS_RESET]
Are you sure you want to reset **all** of your {type} settings to the default values? This cannot be undone. *(__y__es, __n__o)*
This prompt will time out in __30 seconds__.
[C_SETTINGS_RESETERROR]
You provided an invalid input, please try again.
[C_SETTINGS_RESETSUCCESS]
All {type} settings were successfully deleted and set to default.
[C_SETTINGS_USERSETTINGSTITLE]
User Settings
[C_SETTINGS_GUILDSETTINGSTITLE]
Guild Settings
[C_SETTINGS_LISTSETTINGS]
Use `{prefix}setting [setting-name]` to view a description on the setting. Each setting is unique, so make sure to read carefully.
[C_SETTINGS_LISTSETTINGSALT]
Alternatively, you can view user settings by using the command `{prefix}settings -u`
[C_SETTINGS_NONEXISTANT]
That setting does not exist!
//User Command
[C_USER] [C_USER]
**Nickname:** {nickname} **Nickname:** {nickname}
@ -35,4 +61,4 @@ Search result for keyword: `{key}`
Found {matches} matches, displaying {count} Found {matches} matches, displaying {count}
[C_USER_HELP] [C_USER_HELP]
To search server members with similar names use `{prefix}user search < arguments >` To search server members with similar names use `{prefix}user search <arguments..>`

View File

@ -0,0 +1,12 @@
[GENERAL_COMPONENTS]
switch({component}) {
case "command":
"command";
break;
case "setting":
"setting";
break;
case "inhibitor":
"inhibitor":
break;
}

View File

@ -0,0 +1,17 @@
[M_UTILITY_NAME]
Utility
[M_MODERATION_NAME]
Moderation
[M_DEVELOPER_NAME]
Developer
[M_ADMINISTRATOR_NAME]
Administrator
[M_INFORMATION_NAME]
Information
[M_MUSIC_NAME]
Music

View File

@ -0,0 +1,11 @@
[S_GUILDPREFIX_DESCRIPTION]
Customizes your prefix in the guild.
[S_GUILDPREFIX_SUCCESS]
Successfully set the guild prefix to `{prefix}`.
[S_GUILDPREFIX_LENGTH]
The guild prefix cannot exceed {max} characters. `[{length}/{max}]`.
[S_GUILDPREFIX_RESET]
Successfully reset the guild prefix to `{prefix}`.

View File

@ -33,26 +33,86 @@ class MongoDBProvider extends Provider {
type: '', -- The function to use from this class, ex. findOne type: '', -- The function to use from this class, ex. findOne
collection: '', -- Which collection to query from collection: '', -- Which collection to query from
query: { }, -- Actual search query query: { }, -- Actual search query
data: { } -- If the operation is to update or insert something, this is what to insert data: { }, -- If the operation is to update or insert something, this is what to insert
upsert: bool, -- If true and no matching documents are found, insert a new one
options: { },
projection: { } -- The fields that should be returned
} }
*/ */
if (!this._initialized) return { error: true, message: 'MongoDB not connected' }; if (!this._initialized) return { error: true, message: 'MongoDB not connected' };
if (!this[request.type]) return { error: true, message: `Invalid request type, got '${request.type}'` }; if (!this[request.type]) return { error: true, message: `Invalid request type, got '${request.type}'` };
if (!request.collection) return { error: true, message: `You must specify a collection to query!` }; if (!request.collection && request.type !== 'stats') return { error: true, message: `You must specify a collection to query!` };
return this[request.type](request); return this[request.type](request);
} }
remove({ collection, query }) { /**
* Fetches basic statistics about the database or collection
*
* @param {String} collection The collection to query, optional
* @param {Object} options Optional options for the stats method
* @returns
* @memberof MongoDBProvider
*/
stats({ collection, options = { } }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.db.collection(collection).remove(query, (err, result) => { if (!collection || collection.length) {
if (err) reject(err); this.db.stats(options, (error, stats) => {
resolve(result);
if (err) return reject(err);
else {
let { db, collections, objects, averageSize: avgObjSize, dataSize } = stats;
return resolve({ db, collections, objects, averageSize, dataSize });
}
});
} else {
this.db.collection(collection).stats(options, (err, stats) => {
if (err) return reject(err);
else {
let { index: ns, size, count, averageSize: avgObjSize } = stats;
return resolve({ index, size, count, averageSize });
}
});
}
});
}
/**
* Count the objects in
*
* @param {String} collection The collection to query
* @param {Object} query Only documents matching this will be counted, optional
* @param {Object} options Optional options, see mongo docs for these
* @returns
* @memberof MongoDBProvider
*/
count({ collection, query, options }) {
return new Promise((resolve, reject) => {
this.db.collection(collection).countDocuments(query, options, (error, result) => {
if (error)
return reject(error);
else
return resolve(result);
}); });
@ -60,6 +120,38 @@ class MongoDBProvider extends Provider {
} }
/**
* Remove a document from a collection
*
* @param {String} collection The collection to remove from
* @param {Object} query Any documents matching this will be removed
* @returns
* @memberof MongoDBProvider
*/
remove({ collection, query }) {
return new Promise((resolve, reject) => {
this.db.collection(collection).deleteOne(query, (err, result) => {
if (err) return reject(err);
return resolve(result);
});
});
}
/**
* Insert one document to the collection
*
* @param {String} collection The collection to insert into
* @param {Object} data The document to insert
* @returns
* @memberof MongoDBProvider
*/
insertOne({ collection, data }) { insertOne({ collection, data }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -77,17 +169,21 @@ class MongoDBProvider extends Provider {
/** /**
* Find and return the first match * Find and return the first match
*
* @param {String} collection The collection to find from
* @param {Object} query Documents matching this will be returned
* @param {Object} projection Defines which fields to return, { 'key': 1, 'key2': 0 } -- key will return, key2 won't, optional
* @memberof Database * @memberof Database
*/ */
find({ collection, query }) { find({ collection, query, projection }) {
//if(this.manager.debug) this.manager.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`); //if(this.manager.debug) this.manager.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if(!this._initialized) reject(new Error('MongoDB not connected')); if(!this._initialized) reject(new Error('MongoDB not connected'));
this.db.collection(collection).find(query, async (error, cursor) => { this.db.collection(collection).find(query, { projection }, async (error, cursor) => {
if(error) return reject(error); if(error) return reject(error);
return resolve(await cursor.toArray()); return resolve(await cursor.toArray());
@ -101,20 +197,21 @@ class MongoDBProvider extends Provider {
/** /**
* Find and return the first match * Find and return the first match
* *
* @param {String} db The collection in which the data is to be updated * @param {String} collection The collection in which the data is to be updated
* @param {Object} query The filter that is used to find the data * @param {Object} query The filter that is used to find the data
* @param {Object} projection Defines which fields to return, { 'key': 1, 'key2': 0 } -- key will return, key2 won't, optional
* @returns {Object} An object containing the queried data * @returns {Object} An object containing the queried data
* @memberof Database * @memberof Database
*/ */
findOne({ collection, query }) { findOne({ collection, query, projection }) {
//if(this.manager.debug) this.manager.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`); //if(this.manager.debug) this.manager.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if(!this._initialized) reject(new Error('MongoDB not connected')); if(!this._initialized) reject(new Error('MongoDB not connected'));
this.db.collection(collection).findOne(query, async (error, item) => { this.db.collection(collection).findOne(query, { projection }, async (error, item) => {
if(error) return reject(error); if(error) return reject(error);
return resolve(item); return resolve(item);
@ -128,14 +225,14 @@ class MongoDBProvider extends Provider {
/** /**
* Update the first filter match. * Update the first filter match.
* *
* @param {String} db The collection in which the data is to be updated * @param {String} collection The collection in which the data is to be updated
* @param {Object} filter The filter that is used to find the data * @param {Object} query The filter that is used to find the data
* @param {Object} data The updated data * @param {Object} data The updated data
* @param {Boolean} [upsert=false] Create a new entry if no match is found * @param {Boolean} [upsert=true] Create a new entry if no match is found
* @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified * @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified
* @memberof Database * @memberof Database
*/ */
updateOne({ collection, query, data, upsert = false }) { updateOne({ collection, query, data, upsert = true }) {
//if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`); //if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -156,17 +253,47 @@ class MongoDBProvider extends Provider {
} }
/**
* Remove a document property
*
* @param {String} collection The collection to query
* @param {Object} query The filter that is used to find the data
* @param {Array} data Array of fields to remove
* @returns
* @memberof MongoDBProvider
*/
removeProperty({ collection, query, data }) {
return new Promise((resolve, reject) => {
let unset = {};
for (let field in data) unset[field] = '';
this.db.collection(collection).updateOne(query, { $unset: unset }, async (error, result) => {
if(error) return reject(error);
else {
let { matchedCount, modifiedCount } = result;
return resolve({ matched: matchedCount, modified: modifiedCount });
}
});
});
}
/** /**
* Push data to an array * Push data to an array
* *
* @param {string} db The collection to query * @param {string} collection The collection to query
* @param {object} filter The filter to find the document to update * @param {object} query The filter to find the document to update
* @param {object} data The data to be pushed * @param {object} data The data to be pushed
* @param {boolean} [upsert=false] Create a new entry if no match is found * @param {boolean} [upsert=true] Create a new entry if no match is found
* @returns * @returns
* @memberof Database * @memberof Database
*/ */
push({ collection, query, data, upsert = false }) { push({ collection, query, data, upsert = true }) {
//if(this.manager.debug) this.manager.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${JSON.stringify(filter)} and data ${JSON.stringify(data)}`); //if(this.manager.debug) this.manager.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${JSON.stringify(filter)} and data ${JSON.stringify(data)}`);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -187,8 +314,8 @@ class MongoDBProvider extends Provider {
/** /**
* Find a random element from a database * Find a random element from a database
* *
* @param {string} db The collection to query * @param {string} collection The collection to query
* @param {object} [filter={}] The filtering object to narrow down the sample pool * @param {object} [query={}] The filtering object to narrow down the sample pool
* @param {number} [amount=1] Amount of items to return * @param {number} [amount=1] Amount of items to return
* @returns {object} * @returns {object}
* @memberof Database * @memberof Database

View File

@ -28,6 +28,7 @@ class DiscordClient extends Client {
this.localeLoader = new LocaleLoader(this); this.localeLoader = new LocaleLoader(this);
this._options = options; this._options = options;
this._defaultConfig = null;
this._built = false; this._built = false;
process.on('message', this._handleMessage.bind(this)); process.on('message', this._handleMessage.bind(this));
@ -51,7 +52,7 @@ class DiscordClient extends Client {
await this.registry.loadComponents('components/inhibitors/', Inhibitor); await this.registry.loadComponents('components/inhibitors/', Inhibitor);
await this.registry.loadComponents('components/commands/', Command); await this.registry.loadComponents('components/commands/', Command);
await this.registry.loadComponents('components/observers/', Observer); await this.registry.loadComponents('components/observers/', Observer);
// await this.registry.loadComponents('components/settings/', Setting); await this.registry.loadComponents('components/settings/', Setting);
await this.dispatcher.dispatch(); await this.dispatcher.dispatch();
@ -59,6 +60,22 @@ class DiscordClient extends Client {
} }
get defaultConfig() {
if(this._defaultConfig) return this._defaultConfig;
const settings = this.registry.components.filter(c=>c.type === 'setting' && c.resolve === 'GUILD');
let def = {};
for(const setting of settings.values()) {
if(setting.default !== null) {
def = {
...def,
...setting.default
}
}
}
this._defaultConfig = def;
return def;
}
} }
module.exports = DiscordClient; module.exports = DiscordClient;

View File

@ -17,6 +17,28 @@ class Resolver {
return components || []; return components || [];
} }
resolveBoolean(input) {
input = input.toLowerCase();
const truthy = [ 'on', 'true', 'yes', 'enable', 'y' ];
const falsey = [ 'off', 'false', 'no', 'disable', 'n' ];
if(truthy.includes(input)) {
return true;
} else if (falsey.includes(input)) {
return false;
} else {
return null;
}
}
// resolveTrue(input) {
// return [ 'on', 'true' ].includes(input.toLowerCase());
// }
// resolveFalse(input) {
// return [].includes(input.toLowerCase());
// }
async resolveMemberAndUser(string, guild) { async resolveMemberAndUser(string, guild) {

View File

@ -9,21 +9,7 @@ class TransactionHandler {
} }
_receive(message) { send(message, options = {}) {
if(!message.transactionID) return undefined;
const transaction = this.transactions.get(message.transactionID);
if(!transaction) return undefined;
if(message.error) transaction.reject(message.message);
else transaction.resolve(message.result);
this.transactions.delete(message.transactionID);
}
_send(message, options = {}) {
/** Message structure /** Message structure
* { * {
@ -48,6 +34,20 @@ class TransactionHandler {
} }
_receive(message) {
if(!message.transactionID) return undefined;
const transaction = this.transactions.get(message.transactionID);
if(!transaction) return undefined;
if(message.error) transaction.reject(message.message);
else transaction.resolve(message.result);
this.transactions.delete(message.transactionID);
}
get transactionID() { get transactionID() {
return `${[ this.client.shard.ids ]}-${Date.now().toString(36)}`; return `${[ this.client.shard.ids ]}-${Date.now().toString(36)}`;
} }

View File

@ -17,7 +17,8 @@ class PingCommand extends Command {
async execute(message) { async execute(message) {
const ping = this.client.ws.ping.toFixed(0); const ping = this.client.ws.ping.toFixed(0);
return message.respond(`${message.format('PING_COMMAND_RESPONSE')} \`${ping}ms\``, { emoji: 'success' }); //console.log(message.format('C_PING_RESPONSE'))
return message.respond(`${message.format('C_PING_RESPONSE')} \`${ping}ms\``, { emoji: 'success' });
} }
} }

View File

@ -1,4 +1,6 @@
const { Command } = require('../../../../interfaces/'); const { Command, Argument } = require('../../../../interfaces/');
const { stripIndents } = require('common-tags');
class SettingCommand extends Command { class SettingCommand extends Command {
@ -12,7 +14,23 @@ class SettingCommand extends Command {
'settings', 'settings',
'set' 'set'
], ],
showUsage: true arguments: [
new Argument(client, {
name: 'user',
type: 'BOOLEAN',
types: ['VERBAL', 'FLAG'],
default: true
}),
new Argument(client, {
name: 'all',
type: 'BOOLEAN',
types: ['VERBAL', 'FLAG'],
default: true
})
],
memberPermissions: ['ADMINISTRATOR'],
showUsage: true,
}); });
this.client = client; this.client = client;
@ -20,31 +38,113 @@ class SettingCommand extends Command {
} }
//-settings list async execute(message, { params, args }) {
//-settings reset
//-settings walkthrough
//-settings [setting]
async execute(message, { params }) { const type = (!message.guild || args.user) ? 'USER' : 'GUILD';
const target = params[0].toLowerCase(); // params[0] should never be null, see showUsage const target = params[0].toLowerCase(); // params[0] should never be null, see showUsage
if(target === 'list') { if(target === 'list') {
this._listSettings(message); this._listSettings(message, type, Boolean(args.all));
return; return undefined;
} else if(target === 'reset') { } else if(target === 'reset') {
const prompt = await this.message.prompt(message.format('C_SETTING_RESET'), { emoji: 'warning' }); if(message.channel.permissionsFor(message.member).missing('ADMINISTRATOR').length > 0) {
return this._handleReset(prompt, message); await message.respond(message.format('C_SETTINGS_ADMINISTRATORERROR', { type: type.toLowerCase() }), { emoji: 'failure' });
return undefined;
}
const prompt = await message.prompt(message.format('C_SETTINGS_RESET', { type: type.toLowerCase() }), { emoji: 'warning' });
return await this._handleReset(prompt, message, type);
} else if(target === 'walkthrough') { } else if(target === 'walkthrough') {
return; //TODO
return undefined;
} }
const [ setting ] = this.client.resolver.components(params, 'setting', false); const settings = this.client.resolver.components(target, 'setting', false).sort(c=>c.resolve === type);
const [ setting ] = settings;
if(!setting) { if(!setting) {
await message.respond(message.format('C_SETTINGS_NONEXISTANT'), { emoji: 'failure' });
return undefined;
} }
//Setting permission handling
if(setting.clientPermissions.length > 0) {
const missing = message.channel.permissionsFor(message.guild.me).missing(command.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;
}
}
const response = await setting.handle(message, params.splice(1));
message.respond(response.msg, { emoji: response.error ? 'failure' : 'success' });
}
_listSettings(message, type, all) {
if(!message.guild && type === 'GUILD') type = 'USER';
const prefix = message.guild?.prefix || this.client._options.bot.prefix;
let fields = [];
const sorted = this.client.registry.components
.filter(c=>c.type === 'module')
.sort((a, b) => {
const filter = c=>c.type === 'setting';
return b.components.filter(filter) - a.components.filter(filter);
});
for(const module of sorted.values()) {
let field = {
name: module.id,
value: '',
inline: true
};
for(const setting of module.components.values()) {
if(setting.type !== 'setting'
|| (setting.resolve !== type && !all)
|| (setting.restricted && !all)) continue;
field.value += `\`${setting.display}\`\n`;
}
if(field.value) fields.push(field);
}
const embed = {
author: {
name: `${type === 'GUILD' ? message.format('C_SETTINGS_GUILDSETTINGSTITLE') : message.format('C_SETTINGS_USERSETTINGSTITLE')}`,
icon_url: type === 'GUILD' ? message.guild.iconURL() : message.author.avatarURL()
},
description: stripIndents`${message.format('C_SETTINGS_LISTSETTINGS', { prefix })}
${type === 'USER' ? '' : message.format('C_SETTINGS_LISTSETTINGSALT', { prefix })}`,
fields
};
return message.embed(embed);
}
async _handleReset(prompt, message, type) {
if(!prompt) return;
const response = prompt.content.toLowerCase();
const bool = this.client.resolver.resolveBoolean(response);
if(bool === null) return message.respond(message.format('C_SETTINGS_RESETERROR'), { emoji: 'failure' });
if(!bool) return message.respond(message.format('C_SETTINGS_RESETABORT'), { emoji: 'success' });
type === 'USER'
? await message.author._deleteSettings()
: await message.guild._deleteSettings();
return message.respond(message.format('C_SETTINGS_RESETSUCCESS', { type: type.toLowerCase() }), { emoji: 'success' })
} }
} }
module.exports = SettingCommand; module.exports = SettingCommand;

View File

@ -21,7 +21,7 @@ class PingCommand extends Command {
async execute(message) { async execute(message) {
const time1 = new Date().getTime(); 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; }); 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(); const time2 = new Date().getTime();
console.log(time2-time1); console.log(time2-time1);

View File

@ -15,7 +15,7 @@ class ClientPermissions extends Inhibitor {
} }
execute(message, command) { execute(message, command) {
const missing = message.channel.permissionsFor(message.guild.me).missing(command.memberPermissions); const missing = message.channel.permissionsFor(message.guild.me).missing(command.clientPermissions);
if(missing.length > 0) { if(missing.length > 0) {
return super._fail(stripIndents`The command **${command.resolveable}** requires the bot to have permissions to use. return super._fail(stripIndents`The command **${command.resolveable}** requires the bot to have permissions to use.
*Missing: ${missing.join(', ')}*`); *Missing: ${missing.join(', ')}*`);

View File

@ -53,12 +53,14 @@ class CommandHandler extends Observer {
async _getCommand(message, [arg1, arg2, ...args]) { async _getCommand(message, [arg1, arg2, ...args]) {
const prefix = this.client._options.bot.prefix; //Change this for guild prefix settings. if(message.guild) await message.guild.settings();
const prefix = message.guild?.prefix || this.client._options.bot.prefix;
let command = null; let command = null;
let remains = []; let remains = [];
if(arg1 && arg1.startsWith(prefix)) { if(arg1 && arg1.startsWith(prefix)) {
const commandName = arg1.slice(1); const commandName = arg1.slice(prefix.length);
command = await this._matchCommand(message, commandName); command = await this._matchCommand(message, commandName);
remains = [arg2, ...args]; remains = [arg2, ...args];
} else if(arg1 && arg2 && arg1.startsWith('<@')){ } else if(arg1 && arg2 && arg1.startsWith('<@')){
@ -165,7 +167,10 @@ class CommandHandler extends Observer {
const [one,two,...chars] = word.split(''); const [one,two,...chars] = word.split('');
if(one === '-' && two !== '-') { if(one === '-' && two !== '-') {
const name = [ two, ...chars ].join(''); const name = [ two, ...chars ].join('');
if(!keys.includes(name)) continue; if(!keys.includes(name)) {
params.push(word);
continue;
}
currentArgument = shortFlags[name]; currentArgument = shortFlags[name];
if(currentArgument.type === 'BOOLEAN') { if(currentArgument.type === 'BOOLEAN') {
currentArgument.value = currentArgument.default; currentArgument.value = currentArgument.default;
@ -177,11 +182,14 @@ class CommandHandler extends Observer {
return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: true }, message }); return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: true }, message });
} }
continue; continue;
} else if((one === '-' && two === '-') || one === '—') { //Handling for "long dash" on mobile phones x_x } else if((one === '-' && two === '-') || (one === '—')) { //Handling for "long dash" on mobile phones x_x
const name = one === '—' const name = one === '—'
? [ two, ...chars ].join('').toLowerCase() ? [ two, ...chars ].join('').toLowerCase()
: chars.join('').toLowerCase(); //can convert to lowercase now that shortFlags are out of the way. : chars.join('').toLowerCase(); //can convert to lowercase now that shortFlags are out of the way.
if(!keys.includes(name)) continue; if(!keys.includes(name)) {
params.push(word);
continue;
}
currentArgument = longFlags[name]; currentArgument = longFlags[name];
if(currentArgument.type === 'BOOLEAN') { if(currentArgument.type === 'BOOLEAN') {
currentArgument.value = currentArgument.default; currentArgument.value = currentArgument.default;

View File

@ -0,0 +1,54 @@
const { Setting } = require('../../../../interfaces/');
class GuildPrefixSetting extends Setting {
constructor(client, opts = {}) {
super(client, {
name: 'guildPrefix',
index: 'prefix',
module: 'utility',
display: 'prefix',
aliases: [
'prefix'
],
guarded: true,
resolve: 'GUILD',
default: {
prefix: "-"
},
custom: true
});
this.client = client;
}
async handle(message, params, operator) {
if (operator === 'RESET') {
return await super._handleReset(message, params);
}
// let { params, parsedArguments } = await this._parseArguments(params);
let [ prefix ] = params;
const MaxCharacters = 6;
if(prefix.length > MaxCharacters) return {
msg: message.format('S_GPREFIX_LENGTH', { length: prefix.length, max: MaxCharacters }),
error: true
};
await message.guild._updateSettings({ prefix });
return {
msg: message.format(`S_${this.name.toUpperCase()}_SUCCESS`, { prefix }),
error: false
}
}
}
module.exports = GuildPrefixSetting;

View File

@ -15,12 +15,110 @@ const Guild = Structures.extend('Guild', (Guild) => {
async settings() { async settings() {
if (!this._settings) this._settings = this.client.transactionHandler._send({ provider: 'mongodb', request: { collection: 'guild_settings', type: 'findOne', query: { guild: this.id } } }); 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 || { }; if(this._settings instanceof Promise) this._settings = await this._settings || null
if(!this._settings) {
this._settings = this.client.defaultConfig;
}
return this._settings; return this._settings;
} }
/* Database Shortcuts */
async _deleteSettings() { //Delete whole entry - remove
try {
await this.client.transactionHandler.send({
provider: 'mongodb',
request: {
type: 'remove',
collection: 'guilds',
query: {
guildId: this.id
}
}
});
this._settings = this.client.defaultConfig;
this._storageLog(`Database Delete (guild:${this.id}).`);
} catch(error) {
this._storageError(error);
}
}
async _updateSettings(data) { //Update property (upsert true) - updateOne
try {
await this.client.transactionHandler.send({
provider: 'mongodb',
request: {
type: 'updateOne',
collection: 'guilds',
query: {
guildId: this.id
},
data
}
});
this._settings = {
...this._settings,
...data
};
this._storageLog(`Database Update (guild:${this.id}).`);
} catch(error) {
this._storageError(error);
}
}
async _removeSettings(value) { //Remove property
if(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',
query: {
guildId: this.id
},
data: [
value
]
}
});
delete this._settings[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);
}
}
*/
/* Language Formatting */
format(key, parameters = {}) { format(key, parameters = {}) {
const language = this._settings.locale || "en_us"; //this._settings.language or something idk const language = this._settings.locale || "en_us"; //this._settings.language or something idk
@ -33,6 +131,8 @@ const Guild = Structures.extend('Guild', (Guild) => {
} }
/* Resolver Shortcuts */
async resolveMembers(members, strict = false) { async resolveMembers(members, strict = false) {
return await this.client.resolver.resolveMembers(members, this, strict); return await this.client.resolver.resolveMembers(members, this, strict);
@ -72,6 +172,22 @@ const Guild = Structures.extend('Guild', (Guild) => {
} }
/* Logging */
_storageLog(log) {
this.client.logger.debug(log)
}
_storageError(error) {
this.client.logger.error(`Database Error (guild:${this.id}) : \n${error.stack || error}`)
}
get prefix() {
return this._settings.prefix;
}
} }
return ExtendedGuild; return ExtendedGuild;

View File

@ -26,15 +26,17 @@ const Message = Structures.extend('Message', (Message) => {
let language = this.author._settings.locale || 'en_us'; let language = this.author._settings.locale || 'en_us';
if(this.guild && this.guild._settings.locale) language = this.guild._settings.locale; if(this.guild && this.guild._settings.locale) language = this.guild._settings.locale;
parameters.prefix = this.guild && this.guild._settings.prefix ? this.guild._settings.prefix : this.client._options.bot.prefix ; parameters.prefix = this.guild ? this.guild.prefix : this.client._options.bot.prefix;
let template = this.client.localeLoader.template(language, index); //.languages[language][index]; let template = this.client.localeLoader.template(language, index); //.languages[language][index];
if(!template) {
return `**Missing language index \`${language} [${index}]\` in languages. Contact a bot developer about this.**`;
}
for (const [param, val] of Object.entries(parameters)) { for (const [param, val] of Object.entries(parameters)) {
template = template.replace(new RegExp(`{${escapeRegex(param.toLowerCase())}}`, 'gi'), val); template = template.replace(new RegExp(`{${escapeRegex(param.toLowerCase())}}`, 'gi'), val);
} }
template = template.replace(new RegExp(`\r`, 'gi'), '\n');
if(code) { if(code) {
try { try {
template = eval(template); template = eval(template);
@ -86,6 +88,7 @@ const Message = Structures.extend('Message', (Message) => {
if(opts.reply) str = `<@!${this.author.id}> ${str}`; if(opts.reply) str = `<@!${this.author.id}> ${str}`;
} }
//console.log(str)
this._pending = await this.channel.send(str); this._pending = await this.channel.send(str);
return this._pending; return this._pending;
@ -100,6 +103,21 @@ const Message = Structures.extend('Message', (Message) => {
return super.edit(str); return super.edit(str);
} }
async prompt(str, opts) {
if(typeof str === 'string') {
if(opts.emoji) str = `${emojis[opts.emoji]} ${str}`;
if(opts.reply) str = `<@!${this.author.id}> ${str}`;
}
const message = await this.channel.send(str);
return await this.channel.awaitMessages(m => m.author.id === this.author.id, { max: 1, time: 30000, errors: ['time'] })
.then((collected) => {
return collected.first();
})
.catch((error) => {
return null;
});
}
async _showUsage() { async _showUsage() {
//TODO: format this //TODO: format this
return await this.embed({ return await this.embed({

View File

@ -20,7 +20,7 @@ const User = Structures.extend('User', (User) => {
async settings() { async settings() {
if (!this._settings) this._settings = this.client.transactionHandler._send({ provider: 'mongodb', request: { collection: 'user_settings', type: 'findOne', query: { user: this.id } } }); if (!this._settings) this._settings = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'users', type: 'findOne', query: { user: this.id } } });
if (this._settings instanceof Promise) this._settings = await this._settings || {}; if (this._settings instanceof Promise) this._settings = await this._settings || {};
return this._settings; return this._settings;

View File

@ -25,13 +25,42 @@ class Setting extends Component {
this.aliases = opts.aliases || []; this.aliases = opts.aliases || [];
this.resolve = (opts.resolve && Constants.Resolves.includes(opts.resolve)) ? opts.resolve : 'GUILD'; this.resolve = (opts.resolve && Constants.Resolves.includes(opts.resolve)) ? opts.resolve : 'GUILD';
this.default = opts.default; this.default = opts.default;
this.arguments = opts.arguments || [];
this.custom = Boolean(opts.custom);
this.display = opts.display || opts.name;
this.memberPermissions = opts.memberPermissions || []; this.memberPermissions = opts.memberPermissions || [];
this.clientPermissions = opts.clientPermissions || []; this.clientPermissions = opts.clientPermissions || [];
this._commandHandler = null;
} }
async handle() {
this.client.logger.error(`No handle function found in ${this.moduleResolveable}.`);
}
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() {
if(this._commandHandler) return this._commandHandler;
else return this._commandHandler = this.client.registry.components.get('observer:commandHandler');
}
get moduleResolveable() {
return `${this.module.id}:${this.id}`;
}
} }

View File

@ -36,7 +36,7 @@ class Infraction {
async save() { async save() {
await this.client.transactionHandler._send({ await this.client.transactionHandler.send({
provider: 'mongodb', provider: 'mongodb',
request: { request: {
type: 'insertOne', type: 'insertOne',