full commit

This commit is contained in:
nolan 2020-07-19 15:42:21 -07:00
parent ac0492f5c8
commit 4c269f9e7a
34 changed files with 286 additions and 44 deletions

View File

@ -20,6 +20,9 @@ Unmute members from the server.
[C_UNMUTE_MISSINGMEMBERS]
You must provide members to unmute.
[C_UNMUTE_INSUFFICIENTPERMISSIONS]
I don't have permission to manage roles
[C_UNMUTE_CANNOTFINDMUTE]
they were not previously muted
@ -45,6 +48,9 @@ You must provide members to mute.
[C_MUTE_DURATIONEXCEPTION]
The duration must be more than `1 minute` and less than `1 month`.
[C_MUTE_INSUFFICIENTPERMISSIONS]
I don't have permission to manage roles
[C_MUTE_NOMUTEROLE]
You don't have a mute role set in your server, use the command `-set mute` for more information.
@ -68,6 +74,9 @@ Kick provided members from the server.
You must provide members to kick.
[C_KICK_INSUFFICIENTPERMISSIONS]
I don't have permission to kick members
[C_KICK_CANNOTBEKICKED]
they are unable to be kicked
//Softban Command
@ -79,6 +88,9 @@ Primarily used to prune the member's messages in all channels.
You must provide members to softban.
[C_SOFTBAN_INSUFFICIENTPERMISSIONS]
I don't have permission to ban members
[C_SOFTBAN_CANNOTBESOFTBANNED]
they are unable to be softbanned
//Ban Command
@ -111,6 +123,9 @@ Unban provided users from the server.
[C_UNBAN_MISSINGMEMBERS]
You must provide users to unban.
[C_UNBAN_INSUFFICIENTPERMISSIONS]
I don't have permission to unban members
[C_UNBAN_NOTBANNED]
they are not banned
@ -124,6 +139,9 @@ You must provide an amount to prune.
[C_PRUNE_INTEGEREXCEPTION]
The amount must be more than `1` and less than `300`.
[C_PRUNE_INSUFFICIENTPERMISSIONS]
I don't have permission to manage messages
[C_PRUNE_NOTFETCHED]
I was unable to fetch any messages
@ -140,6 +158,9 @@ I had issues deleting those messages
[C_VCKICK_DESCRIPTION]
Kick provided members from their connected voice-channel.
[C_VCKICK_INSUFFICIENTPERMISSIONS]
I don't have permission to move members
[C_VCKICK_NOCHANNEL]
they are not in a voice-channel
@ -153,6 +174,9 @@ You must provide a duration to set the slowmode to.
[C_SLOWMODE_SECONDEXCEPTION]
The duration must be `0 seconds` or more, and less than `6 hours`.
[C_SLOWMODE_INSUFFICIENTPERMISSIONS]
I don't have permission to manage channels
[C_SLOWMODE_NOCHANGE]
the slowmode was already set to that
@ -169,12 +193,28 @@ You must provide arguments to change their nickname to.
[C_NICKNAME_NOCHARACTERS]
they had no hoisted characters in their nickname
[C_NICKNAME_INSUFFICIENTPERMISSIONS]
I don't have permission to manage nicknames
[C_NICKNAME_MISSINGPERMSSIONS]
they have a higher role than I do
//Addrole Command
[C_ADDROLE_DESCRIPTION]
Add roles to a specified member, as long as
Add roles to provided members. The added roles cannot be a higher position than the executor's highest role.
[C_ADDROLE_INSUFFICIENTPERMISSIONS]
I don't have permission to manage roles
[C_ADDROLE_ROLEHIERARCHY]
the provided role(s) have a higher position than yours
//Removerole Command
[C_REMOVEROLE_DESCRIPTION]
[C_REMOVEROLE_DESCRIPTION]
Remove roles to provided members. The removes roles cannot be a higher position than the executor's highest role.
[C_REMOVEROLE_INSUFFICIENTPERMISSIONS]
I don't have permission to manage roles
[C_REMOVEROLE_ROLEHIERARCHY]
the provided role(s) have a higher position than yours

View File

@ -187,7 +187,7 @@ they have hierarchy over you
This action was automatically escalated by auto-moderation.
[INFRACTION_ESCALATIONREASON]
because auto-moderation escalated this infraction.
auto-moderation escalated this infraction.
[INFRACTION_SUCCESS]
Successfully {infraction} {targetType} {target}{text}

View File

@ -362,7 +362,7 @@ class Resolver {
const match = resolveable.match(id);
const [, ch] = match;
const channel = await this.client.channels.fetch(ch);
const channel = await this.client.channels.fetch(ch).catch((e) => {}); //eslint-disable-line no-empty, no-empty-function, no-unused-vars
if (channel && filter(channel)) resolved.push(channel);

View File

@ -9,7 +9,9 @@ class AddroleCommand extends Command {
name: 'addrole',
module: 'moderation',
aliases: [
'roleadd'
'roleadd',
'addroles',
'rolesadd'
],
usage: "<member..> <role..> [reason]",
clientPermissions: ['MANAGE_ROLES'],

View File

@ -67,7 +67,11 @@ class BanCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
this.client = client;

View File

@ -63,7 +63,11 @@ class KickCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
this.client = client;

View File

@ -66,7 +66,11 @@ class MuteCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
}
@ -98,9 +102,9 @@ class MuteCommand extends Command {
}
if(time !== 0) {
if(time < 60 || time > 2629801) { // approx. over 1 month (according to resolveTime)
return message.respond(message.format('C_MUTE_DURATIONEXCEPTION'), { emoji: 'failure' });
}
// if(time < 60 || time > 2629801) { // approx. over 1 month (according to resolveTime)
// return message.respond(message.format('C_MUTE_DURATIONEXCEPTION'), { emoji: 'failure' });
// }
}
return this.client.moderationManager

View File

@ -68,7 +68,11 @@ class NicknameCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
}

View File

@ -23,7 +23,11 @@ class NoteCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
}

View File

@ -9,7 +9,9 @@ class RemoveroleCommand extends Command {
name: 'removerole',
module: 'moderation',
aliases: [
'roleremove'
'roleremove',
'removeroles',
'rolesremove'
],
usage: "<member..> <role..> [reason]",
clientPermissions: ['MANAGE_ROLES'],
@ -24,7 +26,11 @@ class RemoveroleCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 10
}
});
}

View File

@ -25,7 +25,11 @@ class SlowmodeCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 10
}
});
this.client = client;

View File

@ -63,7 +63,11 @@ class SoftbanCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 10
}
});
this.client = client;

View File

@ -28,7 +28,11 @@ class UnbanCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
this.client = client;

View File

@ -27,7 +27,11 @@ class UnmuteCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
}

View File

@ -63,7 +63,11 @@ class VckickCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
this.client = client;

View File

@ -62,7 +62,11 @@ class WarnCommand extends Command {
}
],
guildOnly: true,
showUsage: true
showUsage: true,
throttling: {
usages: 2,
duration: 5
}
});
}

View File

@ -0,0 +1,43 @@
const { Setting } = require('../../../../interfaces/');
class AutoModerationSetting extends Setting {
constructor(client) {
super(client, {
name: 'autoModeration',
module: 'moderation',
aliases: ['autoMod'],
resolve: 'GUILD',
default: {
autoModeration: {
enabled: true,
thresholds: {
'10': {
type: 'MUTE',
length: 3600
}
}
}
}
});
}
async handle(message) {
return {
msg: message.format('S_SILENT_SUCCESS', { value: "fuck yourself" }),
error: false
};
}
fields(guild) {
return {
name: "》Enabled",
value: `\`${guild._settings.silent}\``
};
}
}
module.exports = AutoModerationSetting;

View File

@ -17,7 +17,7 @@ class DmInfractionSetting extends Setting {
enabled: true,
onRemoved: true, //only Kick, Softban, and Ban
custom: {
default: "You were **{infraction}** on the server `{server}`, your infraction details are below."
default: "You were **{infraction}** {from|on} the server `{server}`, your infraction details are below."
//BAN: etc
}
}

View File

@ -54,7 +54,7 @@ const Guild = Structures.extend('Guild', (Guild) => {
}
}
});
this._settings = { ...{}, ...this.client.defaultConfig };
this._settings = { ...{}, ...this.client.defaultConfig }; //Create a new object so settings that change the _settings value won't replicate it towards the defaultConfig.
this._storageLog(`Database Delete (guild:${this.id}).`);
} catch(error) {
this._storageError(error);
@ -62,6 +62,34 @@ const Guild = Structures.extend('Guild', (Guild) => {
return true;
}
async _resetSettings() {
if(!this._settings) await this.settings();
try {
await this.client.transactionHandler.send({
provider: 'mongodb',
request: {
type: 'updateOne',
collection: 'guilds',
query: {
guildId: this.id
},
data: {
caseId: this._settings.caseId,
guildId: this.id
},
upsert: false
}
});
this._settings = {
...this.client.defaultConfig,
caseId: this._settings.caseId
};
this._storageLog(`Database Reset (guild:${this.id}).`);
} catch(error) {
this._storageError(error);
}
}
async _updateSettings(data) { //Update property (upsert true) - updateOne
if(!this._settings) await this.settings();
try {

View File

@ -18,7 +18,7 @@ const GuildMember = Structures.extend('GuildMember', (GuildMember) => {
const filtered = callbacks.filter((e) => e.infraction.type === type
&& e.infraction.target === this.id);
if(filtered.length > 0) return filtered.first();
if(filtered.size > 0) return filtered.first();
const result = await this.client.transactionHandler.send({ //Checking for permanent mutes, won't show up in expirations.
provider: 'mongodb',
request: {

View File

@ -130,11 +130,11 @@ class ModerationManager {
}
if(result && Constants.Hierarchy[infraction.type] <= Constants.Hierarchy[result.type]) {
const escalation = new Constants.Infractions[result.type](this.client, {
executor: message.guild.me,
executor: message.member,
guild: message.guild,
channel: message.channel,
reason: stripIndents`${message.format('INFRACTION_AUTOMODESCALATION')}
${reason}`,
reason: stripIndents`${reason}
*${message.format('INFRACTION_AUTOMODESCALATION')}*`,
message,
target,
duration: result.length,
@ -201,6 +201,9 @@ class ModerationManager {
}
}
const succeededTargets = succeeded.map((s) => s.infraction.target);
const actions = await this._handleArguments(message, succeededTargets); //eslint-disable-line no-unused-vars
let string = "";
for(const [ type, data ] of Object.entries(successes)) {
if(silent) continue;
@ -210,7 +213,7 @@ class ModerationManager {
infraction: dictionary.past,
targetType: `${targetType}${data.targets.length === 1 ? '' : 's'}`,
target: data.targets.map((t) => `**${Util.escapeMarkdown(t)}**`).join(' '),
text: ` ${reason.length > 120 ? `for: \`${reason.substring(0, 117)}...\`` : `\`${reason}\``}`
text: type === original ? ` ${reason.length > 120 ? `for: \`${reason.substring(0, 117)}...\`` : `for: \`${reason}\``}` : ` because \`${reason}\``
})}`;
type === original //eslint-disable-line
? string = `${str}\n${string}`
@ -249,7 +252,9 @@ class ModerationManager {
let messages = await message.channel.messages.fetch({
limit: argument.value
});
messages = messages.filter((m) => users.includes(m.author.id) && m.deleteable);
messages = messages.filter((m) => {
return users.includes(m.author.id) && m.deletable;
});
try {
await message.channel.bulkDelete(messages, true);
} catch(err) {} //eslint-disable-line no-empty
@ -259,6 +264,7 @@ class ModerationManager {
const responses = {};
for(const arg of Object.values(message.arguments)) {
// console.log(arg, targets);
if(actions[arg.name]) {
let action = actions[arg.name](message, arg, targets);
if(action instanceof Promise) action = await action;

View File

@ -10,7 +10,7 @@ class AddroleInfraction extends Infraction {
targetType: 'user',
message: opts.message,
executor: opts.executor.user,
target: opts.target,
target: opts.target.user,
reason: opts.reason || 'N/A',
guild: opts.guild,
channel: opts.channel,
@ -28,6 +28,8 @@ class AddroleInfraction extends Infraction {
});
this.client = client;
this.member = opts.target;
this.executorMember = opts.executor;
}
@ -51,7 +53,7 @@ class AddroleInfraction extends Infraction {
return super._fail('C_ADDROLE_INSUFFICIENTPERMISSIONS');
}
const { highest } = this.executor.roles;
const { highest } = this.executorMember.roles;
const filtered = this.data.roles.filter((r) => r.comparePositionTo(highest) < 0);
if(filtered.length === 0) {
return super._fail('C_ADDROLE_ROLEHIERARCHY');

View File

@ -45,7 +45,12 @@ class Kick extends Infraction {
verify() {
if(!this.member.kickable) return this._fail('C_KICK_INSUFFICIENTPERMISSIONS');
const missing = this.channel.permissionsFor(this.guild.me).missing(['KICK_MEMBERS']);
if(missing.length > 0) {
return super._fail('C_KICK_INSUFFICIENTPERMISSIONS');
}
if(!this.member.kickable) return this._fail('C_KICK_CANNOTBEKICKED');
return super._verify();
}

View File

@ -91,6 +91,11 @@ class Mute extends Infraction {
async verify() {
const missing = this.channel.permissionsFor(this.guild.me).missing(['MANAGE_ROLES']);
if(missing.length > 0) {
return super._fail('C_MUTE_INSUFFICIENTPERMISSIONS');
}
if(this.guild._settings.mute.type !== 2) {
if(!this.guild._settings.mute.role) return this._fail('C_MUTE_NOMUTEROLE');

View File

@ -71,6 +71,11 @@ class Nickname extends Infraction {
verify() {
const missing = this.channel.permissionsFor(this.guild.me).missing(['MANAGE_NICKNAMES']);
if(missing.length > 0) {
return super._fail('C_NICKNAME_INSUFFICIENTPERMISSIONS');
}
const { highest } = this.member.roles;
if(highest.comparePositionTo(this.guild.me.roles.highest) > 0) {
return this._fail('C_NICKNAME_MISSINGPERMISSIONS');

View File

@ -172,6 +172,17 @@ class Prune extends Infraction {
description() {
return `\n${this.guild.format('INFRACTION_DESCRIPTIONAMOUNT', { amount: this.data.amount })}`;
}
verify() {
const missing = this.channel.permissionsFor(this.guild.me).missing(['MANAGE_MESSAGES']);
if(missing.length > 0) {
return super._fail('C_PRUNE_INSUFFICIENTPERMISSIONS');
}
return super._verify();
}
}

View File

@ -28,6 +28,8 @@ class RemoveroleInfraction extends Infraction {
});
this.client = client;
this.member = opts.target;
this.executorMember = opts.executor;
}
@ -51,10 +53,10 @@ class RemoveroleInfraction extends Infraction {
return super._fail('C_REMOVEROLE_INSUFFICIENTPERMISSIONS');
}
const { highest } = this.executor.roles;
const { highest } = this.executorMember.roles;
const filtered = this.data.roles.filter((r) => r.comparePositionTo(highest) < 0);
if(filtered.length === 0) {
return super._fail('C_REMOVEROLE_ROLEHIERARCHY');
return super._fail('C_REMOVEROLE_ROLEHIERARCHY', { plural: filtered.length === 1 ? '' : 's' });
}
this.data.roles = filtered;

View File

@ -54,6 +54,17 @@ class Slowmode extends Infraction {
return `\n${this.guild.format('INFRACTION_DESCRIPTIONDURATION', { duration: Util.duration(this.data.seconds) })}`;
}
verify() {
const missing = this.channel.permissionsFor(this.guild.me).missing(['MANAGE_CHANNELS']);
if(missing.length > 0) {
return super._fail('C_SLOWMODE_INSUFFICIENTPERMISSIONS');
}
return super._verify();
}
}
module.exports = Slowmode;

View File

@ -52,9 +52,14 @@ class Softban extends Infraction {
}
verify() {
const missing = this.channel.permissionsFor(this.guild.me).missing(['BAN_MEMBERS']);
if(missing.length > 0) {
return super._fail('C_SOFTBAN_INSUFFICIENTPERMISSIONS');
}
if(this.member instanceof GuildMember) {
if(!this.member.bannable) return this._fail('C_SOFTBAN_INSUFFICIENTPERMISSIONS');
if(!this.member.bannable) return this._fail('C_SOFTBAN_CANNOTBESOFTBANNED');
}
return super._verify();

View File

@ -51,6 +51,12 @@ class Unban extends Infraction {
}
async verify() {
const missing = this.channel.permissionsFor(this.guild.me).missing(['BAN_MEMBERS']);
if(missing.length > 0) {
return super._fail('C_UNBAN_INSUFFICIENTPERMISSIONS');
}
let ban = null;
try {
ban = await this.guild.fetchBan(this.member.id);

View File

@ -47,9 +47,9 @@ class Mute extends Infraction {
} else {
mute = await this.member._getExpiration('MUTE');
if(mute) {
removedRoles = mute.data.removedRoles; //eslint-disable-line prefer-destructuring
muteType = mute.data.muteType; //eslint-disable-line prefer-destructuring
role = mute.data.muteRole; //eslint-disable-line prefer-destructuring
removedRoles = mute.infraction.data.removedRoles; //eslint-disable-line prefer-destructuring
muteType = mute.infraction.data.muteType; //eslint-disable-line prefer-destructuring
role = mute.infraction.data.muteRole; //eslint-disable-line prefer-destructuring
}
}
@ -104,6 +104,19 @@ class Mute extends Infraction {
}
async verify() {
const missing = this.channel.permissionsFor(this.guild.me).missing(['MANAGE_ROLES']);
if(missing.length > 0) {
return super._fail('C_UNMUTE_INSUFFICIENTPERMISSIONS');
}
return super._verify();
}
}
module.exports = Mute;

View File

@ -44,9 +44,15 @@ class Vckick extends Infraction {
}
verify() {
if(!this.member.voice.channel) return this._fail('C_VCKICK_NOCHANNEL');
const missing = this.channel.permissionsFor(this.guild.me).missing(['MOVE_MEMBERS']);
if(missing.length > 0) {
return super._fail('C_VCKICK_INSUFFICIENTPERMISSIONS');
}
if(!this.member.voice.channel) return this._fail('C_VCKICK_NOCHANNEL');
return super._verify();
}
}

View File

@ -1,7 +1,8 @@
const { Util } = require('../../../util/');
const Constants = {
MaxCharacters: 1024 // Max embed description is 2048 characters, however some of those description characters are going to usernames, types, filler text, etc.
MaxCharacters: 1024, // Max embed description is 2048 characters, however some of those description characters are going to usernames, types, filler text, etc.
RemovedInfractions: ['BAN', 'SOFTBAN', 'KICK']
};
class Infraction {
@ -75,7 +76,8 @@ class Infraction {
message = message
.replace(/\{(guild|server)\}/ugim, this.guild.name)
.replace(/\{user\}/ugim, this.target.tag)
.replace(/\{infraction\}/ugim, this.dictionary.past); //add more if you want i should probably add a better system for this...
.replace(/\{infraction\}/ugim, this.dictionary.past)
.replace(/\{from\|on\}/ugim, Constants.RemovedInfractions.includes(this.type) ? 'from' : 'on'); //add more if you want i should probably add a better system for this...
try {
this.target.send(message, {
embed: this.embed(true)
@ -129,7 +131,7 @@ class Infraction {
description += this.description(dm);
}
if(!this.silent && (this.message || this.hyperlink)) {
if((!this.silent && (this.message || this.hyperlink)) && (dm && !Constants.RemovedInfractions.includes(this.type))) {
description += `\n${this.guild.format('INFRACTION_DESCRIPTIONJUMPTO', { name: this.hyperlink ? 'Case' : 'Message', link: this.hyperlink ? this.hyperlink : `https://discord.com/channels/${this.guild.id}/${this.channel.id}/${this.message.id}` })}`;
}
@ -220,7 +222,7 @@ class Infraction {
}
_verify() {
if(this.guild && this.guild._settings.protection) {
if(this.guild && this.guild._settings.protection.enabled) {
const executorHighest = this.executor.roles.highest;
const targetHighest = this.member.roles.highest;
if(executorHighest.comparePositionTo(targetHighest) > 0) {

View File

@ -13,7 +13,7 @@ class Util {
static downloadAsBuffer(source) {
return new Promise((resolve, reject) => {
fetch(source).then(res => {
fetch(source).then((res) => {
if(res.ok) resolve(res.buffer());
else reject(res.statusText);
});