This commit is contained in:
nolan 2020-05-23 23:50:42 -04:00
commit b041be78bd
13 changed files with 399 additions and 67 deletions

View File

@ -37,6 +37,7 @@ __**Warning:** This is a long embed.__
__**{component} HELP**__ __**{component} HELP**__
{desc} {desc}
**Example usage**
{text} {text}

View File

@ -24,4 +24,17 @@ Arguments
Current Settings Current Settings
[GENERAL_SETTINGRESET] [GENERAL_SETTINGRESET]
Successfully reset the setting {setting} to the default value. Successfully reset the setting **{setting}** to the default value.
[GENERAL_ADDED]
added
[GENERAL_REMOVED]
removed
[ERR_INVALID_METHOD]
Method `{method}` is invalid!
[PREMIUM_2]
Sorry! This is a tier 2 premium feature.
Current server tier: `{tier}`

View File

@ -1,6 +1,6 @@
//modlogs setting //modlogs setting
[S_MODLOGS_DESCRIPTION] [S_MODERATIONLOG_DESCRIPTION]
Define the channel to which moderation logs are sent. The setting also allows you to exclude types of actions from being logged in the channel. Define the channel to which moderation logs are sent. The setting also allows you to exclude types of actions from being logged in the channel.
[S_MODLOGS_CHANNEL404] [S_MODLOGS_CHANNEL404]
@ -9,9 +9,58 @@ Define the channel to which moderation logs are sent. The setting also allows yo
[S_MODLOGS_CHANNEL_SUCCESS] [S_MODLOGS_CHANNEL_SUCCESS]
Successfully set the modlogs channel to {channel}. Successfully set the modlogs channel to {channel}.
[S_MODLOGS_EXCLUDE404] [S_MODLOGS_ARGS404]
Missing arguments. Missing arguments.
[S_MODLOGS_LIST]
The following infractions are set to be logged:
`{list}`
[S_MODLOGS_REMOVE]
The followin infraction types will no longer be logged:
`{list}`
[S_MODLOGS_ADD]
The followin infraction types will now be logged:
`{list}`
//CHATLOGS SETTING
[S_CHATLOGS_DESCRIPTION]
Configure message logging for your server.
[S_CHATLOGS_ROLES_LIST]
The following roles are ignored by chatlogs:
{roles}
[S_CHATLOGS_CHANNELS_LIST]
The following channels are ignored by chatlogs:
{channels}
[S_CHATLOGS_ROLES]
Successfully {action} the following roles:
{changed}
[S_CHATLOGS_CHANNELS]
Successfully {action} the following channels:
{changed}
[S_CHATLOGS_ATTACHMENTS]
Successfully turned attachment logging {changed}
[S_CHATLOGS_TOGGLE]
switch({toggle}) {
case true:
'on';
break;
case false:
'off';
break;
}
[S_CHATLOGS_RESET]
Successfully reset the chatlogs setting.
//mute Setting //mute Setting
[S_MUTE_DESCRIPTION] [S_MUTE_DESCRIPTION]

View File

@ -39,6 +39,62 @@ class Resolver {
// resolveFalse(input) { // resolveFalse(input) {
// return [].includes(input.toLowerCase()); // return [].includes(input.toLowerCase());
// } // }
/**
* Resolves methods used primarily for settings, also deals with appending the arguments into existing lists
*
* @param {Array<String>} args The incoming arguments with the first element being the method ex. ['add','ban','kick']
* @param {Array<String>} valid An array of items to compare to, if an argument doesn't exist in this array it'll be skipped over
* @param {Array<String>} [existing=[]] Existing values in the array, valid elements will be appended to this
* @returns {Object}
* @memberof Resolver
*/
resolveMethod(args, valid, existing = []) {
const methods = {
list: ['view', 'list', '?'],
add: ['add', '+'],
remove: ['remove', 'delete', '-']
};
if (!args.length) return false;
// eslint-disable-next-line prefer-const
let [method, ...rest] = args;
method = method.toLowerCase();
if (!rest.length) {
if (methods.list.includes(method)) return { method: 'list', rest };
if (methods.add.includes(method)) return { method: 'add', rest };
if (methods.remove.includes(method)) return { method: 'remove', rest };
}
if (methods.list.includes(method)) {
return { method: 'list' };
} else if (methods.add.includes(method)) {
const added = [];
for (let elem of rest) {
elem = elem.toLowerCase();
if (existing.includes(elem) || valid && !valid.includes(elem)) continue;
added.push(elem);
existing.push(elem);
}
return { rest, method: 'add', changed: added, result: existing };
} else if (methods.remove.includes(method)) {
const removed = [];
for (let elem of rest) {
elem = elem.toLowerCase();
if (!existing.includes(elem) || removed.includes(elem) || valid && !valid.includes(elem)) continue;
removed.push(elem);
existing.splice(existing.indexOf(elem), 1);
}
return { rest, method: 'remove', changed: removed, result: existing };
}
return false;
}
async resolveMemberAndUser(string, guild) { async resolveMemberAndUser(string, guild) {

View File

@ -31,14 +31,14 @@ class HelpCommand extends Command {
description: message.format('C_HELP_TEMPLATE', { description: message.format('C_HELP_TEMPLATE', {
desc: message.format(result.description), desc: message.format(result.description),
component: result.name.toUpperCase(), component: result.name.toUpperCase(),
text: result.examples.map(ex => `\`{prefix}${result.name} ${ex}\``).join('\n') text: result.examples.map(ex => `\`{prefix}${result.type === 'command' ? result.name.toLowerCase() : `settings ${result.name.toLowerCase()}`} ${ex}\``).join('\n')
})//, }),
// fields: [ fields: [
// { {
// name: '', name: 'Aliases',
// value: message.format(result.examples) value: result.aliases.map(al => al.toLowerCase()).join(', ')
// } }
// ] ]
}); });
} }

View File

@ -73,7 +73,7 @@ class SettingsCommand extends Command {
} }
} }
setting._caller = target; message._settingCaller = target;
if(setting.resolve === 'GUILD' && !this._checkAdministrator(message)) return undefined; //does not have admin if(setting.resolve === 'GUILD' && !this._checkAdministrator(message)) return undefined; //does not have admin
const parameters = params.splice(1); const parameters = params.splice(1);
@ -83,7 +83,8 @@ class SettingsCommand extends Command {
} }
if(parameters.join(' ').toLowerCase() === 'reset') { if(parameters.join(' ').toLowerCase() === 'reset') {
return await setting._handleReset(message); const { msg } = await setting._handleReset(message);
return message.respond(msg, { emoji: 'success' });
} }
const response = await setting.handle(message, parameters); const response = await setting.handle(message, parameters);

View File

@ -161,6 +161,8 @@ class CommandHandler extends Observer {
const { shortFlags, longFlags, keys } = await this._createFlags(passedArguments); const { shortFlags, longFlags, keys } = await this._createFlags(passedArguments);
const regex = new RegExp(`([0-9]*)(${Object.keys(longFlags).map(k=>escapeRegex(k)).join('|')})([0-9]*)`, 'i'); const regex = new RegExp(`([0-9]*)(${Object.keys(longFlags).map(k=>escapeRegex(k)).join('|')})([0-9]*)`, 'i');
//console.log('args')
//console.log(args)
let parsedArguments = []; let parsedArguments = [];
let params = []; let params = [];
@ -217,6 +219,7 @@ class CommandHandler extends Observer {
continue; continue;
} else { } else {
let match = regex.exec(word); let match = regex.exec(word);
//console.log(match)
if(match && match[2]) { if(match && match[2]) {
currentArgument = longFlags[match[2]]; currentArgument = longFlags[match[2]];
if(params.length > 0 && ['INTEGER', 'FLOAT'].includes(currentArgument.type)) { //15 pts if(params.length > 0 && ['INTEGER', 'FLOAT'].includes(currentArgument.type)) { //15 pts
@ -258,8 +261,10 @@ class CommandHandler extends Observer {
continue; continue;
} }
} else { } else {
//console.log(currentArgument?.name)
if(currentArgument) { if(currentArgument) {
const error = await this._handleTypeParsing(currentArgument, word, guild); const error = await this._handleTypeParsing(currentArgument, word, guild);
//console.log(error)
if(error) { if(error) {
if(currentArgument.default !== null) { if(currentArgument.default !== null) {
params.push(word); params.push(word);

View File

@ -0,0 +1,197 @@
const { Setting } = require('../../../../interfaces/');
class Chatlogs extends Setting {
constructor(client) {
super(client, {
name: 'messageLogs',
module: 'moderation',
index: 'chatlogs',
aliases: [
'chatLog',
'chatLogs',
'msgLogs'
],
guarded: true,
resolve: 'GUILD',
examples: [
'chatlogs roles <add|remove|list> <role>',
'chatlogs channels <add|remove|list> <role>',
'chatlogs reset',
'chatlogs attachments <on|off>',
'chatlogs #channel'
],
default: {
chatlogs: {
channel: undefined,
ignoredChannels: [],
ignoredRoles: [],
attachments: false
}
}
});
this.client = client;
}
async handle(message, params) {
// eslint-disable-next-line init-declarations
let index, changes, action;
// eslint-disable-next-line prefer-const
let [method, ...args] = params;
method = method.toLowerCase();
const setting = message.guild._settings.chatlogs || this.default.chatlogs;
const { guild } = message;
if (method === 'roles') {
const response = this.resolveMethod(args);
if (response) {
if (response.method === 'add') {
const roles = await guild.resolveRoles(response.rest);
setting.ignoredRoles = [...setting.ignoredRoles, ...roles.filter(r => !setting.ignoredRoles.includes(r.id)).map(r => r.id)];
action = 'GENERAL_ADDED';
index = 'S_CHATLOGS_ROLES';
changes = roles.map(r => r.name);
} else if (response.method === 'remove') {
const roles = await guild.resolveRoles(response.rest);
const _roles = roles.map(r => r.id);
setting.ignoredRoles = setting.ignoredRoles.filter(r => !_roles.includes(r));
action = 'GENERAL_REMOVED';
index = 'S_CHATLOGS_ROLES';
changes = roles.map(r => r.name);
} else if (response.method === 'list') {
const roles = await guild.resolveRoles(setting.ignoredRoles);
return {
msg: message.format('S_CHATLOGS_ROLES_LIST', { roles: roles.map(r => r.name).join(', ') })
};
}
} else {
return {
msg: message.format('ERR_INVALID_METHOD', { method })
};
}
} else if (method === 'channels') {
const response = this.resolveMethod(args);
if (response) {
if (response.method === 'add') {
const channels = guild.resolveChannels(response.rest);
setting.ignoredChannels = [...setting.ignoredChannels, ...channels.filter(c => !setting.ignoredChannels.includes(c.id)).map(c => c.id)];
action = 'GENERAL_ADDED';
index = 'S_CHATLOGS_CHANNELS';
changes = channels.map(c => c.name);
} else if (response.method === 'remove') {
const channels = guild.resolveChannels(response.rest);
const _channels = channels.map(c => c.id);
setting.ignoredChannels = setting.ignoredChannels.filter(c => !_channels.includes(c));
action = 'GENERAL_REMOVED';
index = 'S_CHATLOGS_CHANNELS';
changes = channels.map(c => c.name);
} else if (response.method === 'list') {
return {
msg: message.format('S_CHATLOGS_LIST')
};
}
changes = response.changed;
} else {
const channels = guild.resolveChannels(setting.ignoredChannels);
return {
msg: message.format('S_CHATLOGS_ROLES_LIST', { roles: channels.map(r => r.name).join(', ') })
};
}
} else if (method === 'attachments') {
if (guild.premium < 2) return {
msg: message.format('PREMIUM_2', { tier: guild.premium }),
error: true
}
const [bool] = args;
const result = this.client.resolver.resolveBoolean(bool);
if (result) {
setting.attachments = true;
index = 'S_CHATLOGS_ATTACHMENTS';
changes = message.format('S_CHATLOGS_TOGGLE', { toggle: true }, true);
} else {
setting.attachments = false;
index = 'S_CHATLOGS_ATTACHMENTS';
changes = message.format('S_CHATLOGS_TOGGLE', { toggle: false }, true);
}
} else {
const channel = message.guild.resolveChannel(args[0]);
if (!channel) return {
msg: message.format('S_CHATLOGS_CHANNEL404'),
error: true
};
setting.channel = channel.id;
}
await message.guild._updateSettings({ [this.index]: setting });
return {
msg: message.format(index, { changed: changes instanceof Array ? changes?.join(', ') : changes || undefined, action: message.format(action) })
};
}
async fields(guild) {
const roles = guild._settings[this.index]?.ignoredRoles ? await Promise.all(guild._settings[this.index].ignoredRoles.map(async (role) => guild.resolveRole(role))) : undefined;
return [
{
name: 'Channel',
value: guild.resolveChannel(guild._settings[this.index]?.channel) || 'N/A',
inline: true
},
{
name: 'Ignored roles',
value: roles?.map((r) => r.name).join(', ') || 'N/A',
inline: true
},
{
name: 'Ignored channels',
value: guild._settings[this.index]?.ignoredChannels.map((c) => guild.resolveChannel(c).name).join(', ') || 'N/A',
inline: true
}
];
}
}
module.exports = Chatlogs;

View File

@ -1,5 +1,9 @@
const { Setting } = require('../../../../interfaces/'); const { Setting } = require('../../../../interfaces/');
const CONSTANTS = {
INFRACTIONS: ['note', 'warn', 'mute', 'unmute', 'lockdown', 'lockdownend', 'kick', 'ban', 'unban', 'vcmute', 'vcunmute', 'vckick', 'vcban', 'vcunban', 'prune', 'slowmode', 'dehoist', 'addrole', 'removerole', 'nickname']
};
class Modlogs extends Setting { class Modlogs extends Setting {
constructor(client) { constructor(client) {
@ -14,24 +18,16 @@ class Modlogs extends Setting {
], ],
guarded: true, guarded: true,
resolve: 'GUILD', resolve: 'GUILD',
//custom: true, index: 'modlogs',
examples: [ examples: [
'modlog #moderation-log', 'modlogs <add|remove|list> <infraction-type..>',
'modlog reset' 'modlogs #moderation-log',
], 'modlogs reset'
arguments: [
{
name: 'exclude',
type: 'STRING',
types: ['VERBAL'],
requiredValue: true,
infinite: true
}
], ],
default: { default: {
modlogs: { modlogs: {
channel: undefined, channel: undefined,
exclude: [] infractions: ['warn','mute','unmute','lockdown','lockdownend','kick','ban','unban','vcmute','vcunmute','vckick','vcban','vcunban']
} }
} }
}); });
@ -42,52 +38,52 @@ class Modlogs extends Setting {
async handle(message, args) { async handle(message, args) {
const { params, parsedArguments } = await this._parseArguments(args, message.guild, true); const setting = message.guild._settings.modlogs || this.default.modlogs;
let setting = message.guild._settings.modlogs || { }; const response = this.resolveMethod(args, CONSTANTS.INFRACTIONS, setting.infractions);
if (parsedArguments.exclude) { if (response) {
if (!parsedArguments.exclude.value.length) return {
msg: message.format('S_MODLOGS_EXCLUDE404'), if (args.length < 2 && response.method !== 'list') return {
msg: message.format('S_MODLOGS_ARGS404'),
error: true error: true
} }
let index;
if (response.method === 'add') {
setting.infractions = response.result;
index = 'S_MODLOGS_ADD';
} else if (response.method === 'remove') {
setting.infractions = response.result;
index = 'S_MODLOGS_REMOVE';
} else if (response.method === 'list') {
return {
msg: message.format('S_MODLOGS_LIST', { list: setting.infractions.join('`, `') })
}
}
if(response.changed.length) await message.guild._updateSettings({ [this.index]: setting });
return { msg: message.format(index, { list: response.changed.length ? response.changed.join('`, `') : 'N/A' }) };
} else { } else {
let [channel] = params; let [channel] = params;
if (channel === 'reset') {
channel = message.guild.resolveChannel(channel);
if (!channel) return {
msg: message.format('S_MODLOGS_CHANNEL404', { val: params[0] }),
error: true
};
setting = {}; setting.channel = channel.id;
await message.guild._updateSettings({ [this.index]: setting }); await message.guild._updateSettings({ [this.index]: setting }); return {
return { msg: message.format('S_MODLOGS_CHANNEL_SUCCESS', { channel: channel.name }),
msg: message.format('S_MODLOGS_RESET'), error: false
error: false };
};
} else {
channel = message.guild.resolveChannel(channel);
if (!channel) return {
msg: message.format('S_MODLOGS_CHANNEL404', { val: params[0] }),
error: true
};
setting.channel = channel.id;
await message.guild._updateSettings({ [this.index]: setting }); return {
msg: message.format('S_MODLOGS_CHANNEL_SUCCESS', { channel: channel.name }),
error: false
};
}
} }
} }
data(guild) {
return `**Prefix:** \`${guild.prefix}\``;
}
fields(guild) { fields(guild) {
return [ return [
{ {
@ -96,8 +92,8 @@ class Modlogs extends Setting {
inline: true inline: true
}, },
{ {
name: 'Excluded types', name: 'Enabled infractions',
value: guild._settings[this.index]?.exclude.join(', ') || 'N/A', value: guild._settings[this.index]?.infractions.join(', ') || 'N/A',
inline: true inline: true
} }
]; ];

View File

@ -54,7 +54,7 @@ class MuteSetting extends Setting {
const { params, parsedArguments } = await this._parseArguments(args, message.guild); const { params, parsedArguments } = await this._parseArguments(args, message.guild);
args = params; args = params;
if(['mutetype', 'mutedtype'].includes(this._caller) || parsedArguments.type) { if(['mutetype', 'mutedtype'].includes(message._settingCaller) || parsedArguments.type) {
const num = args[0].toLowerCase() === 'type' ? args[1] || 0 : args[0]; const num = args[0].toLowerCase() === 'type' ? args[1] || 0 : args[0];
const number = parseInt(num); const number = parseInt(num);
if(isNaN(number)) return { if(isNaN(number)) return {
@ -83,7 +83,7 @@ class MuteSetting extends Setting {
let role = null; let role = null;
let updatedPermissions = false; let updatedPermissions = false;
let created = false; let created = false;
if(parsedArguments.create || this._caller === 'createmute') { if(parsedArguments.create || message._settingCaller === 'createmute') {
const missing = message.channel.permissionsFor(message.guild.me).missing('MANAGE_ROLES'); const missing = message.channel.permissionsFor(message.guild.me).missing('MANAGE_ROLES');
if(missing.length > 0) return { if(missing.length > 0) return {
msg: message.format('S_MUTE_ROLEMISSINGPERMISSION'), msg: message.format('S_MUTE_ROLEMISSINGPERMISSION'),

View File

@ -26,7 +26,7 @@ class GuildPrefixSetting extends Setting {
async handle(message, params) { async handle(message, params) {
const [ prefix ] = params; let [ prefix ] = params;
const MaxCharacters = 6; const MaxCharacters = 6;
if(prefix.length > MaxCharacters) return { if(prefix.length > MaxCharacters) return {
@ -39,7 +39,7 @@ class GuildPrefixSetting extends Setting {
error: true error: true
}; };
if (prefix === 'reset') prefix = this.default.prefix; if (prefix === 'reset') ({ prefix } = this.default);
await message.guild._updateSettings({ [this.index]: prefix }); await message.guild._updateSettings({ [this.index]: prefix });
return { return {

View File

@ -11,6 +11,7 @@ const Guild = Structures.extend('Guild', (Guild) => {
this._settings = null; //internal cache of current guild's settings; should ALWAYS stay the same as database. 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. this._permissions = null; //internal cache, should always match database.
this.premium = 0;
} }
@ -78,7 +79,7 @@ const Guild = Structures.extend('Guild', (Guild) => {
async _removeSettings(value) { //Remove property async _removeSettings(value) { //Remove property
if(this.client.defaultConfig[value]) { if(this.client.defaultConfig[value]) {
await this._updateSettings(this.client.defaultConfig[value]); await this._updateSettings({ [value]: this.client.defaultConfig[value] });
return undefined; return undefined;
} }
try { try {

View File

@ -47,13 +47,26 @@ class Setting extends Component {
return { parsedArguments: response.parsedArguments, params: response.newArgs, error: false }; return { parsedArguments: response.parsedArguments, params: response.newArgs, error: false };
} }
/**
* Resolves methods used primarily for settings, also deals with appending the arguments into existing lists
*
* @param {Array<String>} args The incoming arguments with the first element being the method ex. ['add','ban','kick']
* @param {Array<String>} valid An array of items to compare to, if an argument doesn't exist in this array it'll be skipped over
* @param {Array<String>} [existing=[]] Existing values in the array, valid elements will be appended to this
* @returns {Object}
* @memberof Resolver
*/
resolveMethod(args, valid, existing) {
return this.client.resolver.resolveMethod(args, valid, existing);
}
reason(executor) { reason(executor) {
return `[${this.moduleResolveable}] Executed by ${executor.tag} (${executor.id}).`; return `[${this.moduleResolveable}] Executed by ${executor.tag} (${executor.id}).`;
} }
async _handleReset(message) { async _handleReset(message) {
await message.guild._removeSettings(this.index); await message.guild._removeSettings(this.index);
const msg = message.format('GENERAL_SETTINGRESET', { setting: this.resolveable }); const msg = message.format('GENERAL_SETTINGRESET', { setting: this.name.toLowerCase() });
return { return {
error: false, error: false,
msg msg