2020-07-04 12:23:10 +02:00
|
|
|
const { Infraction } = require('../interfaces/');
|
|
|
|
const { Collection } = require('../../../util/');
|
|
|
|
|
|
|
|
const Arguments = ['users', 'bots', 'text', 'startswith', 'endswith', 'emojis', 'reactions', 'emojis', 'reactions', 'images', 'attachments'];
|
|
|
|
|
|
|
|
class Prune extends Infraction {
|
|
|
|
|
|
|
|
constructor(client, opts = {}) {
|
|
|
|
|
|
|
|
super(client, {
|
|
|
|
type: 'PRUNE',
|
|
|
|
targetType: 'channel',
|
|
|
|
message: opts.message,
|
|
|
|
arguments: opts.arguments,
|
|
|
|
executor: opts.executor.user,
|
|
|
|
target: opts.target,
|
|
|
|
reason: opts.reason || 'N/A',
|
|
|
|
guild: opts.guild,
|
|
|
|
channel: opts.channel,
|
|
|
|
silent: opts.silent,
|
|
|
|
color: 0xdb36fc,
|
|
|
|
dictionary: {
|
|
|
|
past: 'pruned',
|
|
|
|
present: 'prune'
|
|
|
|
},
|
2020-07-16 09:54:39 +02:00
|
|
|
points: opts.points,
|
|
|
|
expiration: opts.expiration,
|
2020-07-04 12:23:10 +02:00
|
|
|
data: opts.data
|
|
|
|
});
|
|
|
|
|
|
|
|
this.client = client;
|
|
|
|
this.channel = opts.target;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async execute() {
|
|
|
|
|
|
|
|
const { amount, message } = this.data;
|
|
|
|
let messages = await this.fetchMessages(message, amount); //Collection, not array.
|
|
|
|
if(messages.size === 0) return this._fail('C_PRUNE_NOTFETCHED');
|
|
|
|
|
|
|
|
const hasOld = messages.some((m) => m.createdTimestamp >= Date.now()+1209600000); //I hope Node.js can handle these large of numbers.. e_e (2 weeks)
|
|
|
|
const hasUndeletable = messages.some((m) => m.deletable);
|
|
|
|
|
|
|
|
messages = messages.filter((m) => m.deletable);
|
|
|
|
if(messages.size === 0) return this._fail('C_PRUNE_NOTDELETABLE');
|
|
|
|
|
|
|
|
const filtered = await this.filterMessages(messages);
|
|
|
|
if(filtered.size === 0) return this._fail('C_PRUNE_NOFILTERRESULTS');
|
|
|
|
|
|
|
|
const deleted = await this.deleteMessages(filtered);
|
|
|
|
if(deleted === 0) return this._fail('C_PRUNE_NODELETE');
|
|
|
|
|
|
|
|
await this.handle();
|
|
|
|
return this._succeed('C_PRUNE_AMOUNT', {
|
|
|
|
hasOld: deleted > amount ? hasOld : false,
|
|
|
|
hasUndeletable: deleted > amount ? hasUndeletable : false,
|
|
|
|
amount: deleted
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async deleteMessages(messages) {
|
|
|
|
|
|
|
|
let amount = 0;
|
|
|
|
const bulkDelete = async(messages) => {
|
|
|
|
try {
|
|
|
|
const deleteThese = messages.splice(0, 100);
|
|
|
|
const result = await this.target.bulkDelete(deleteThese, true); //Filtering old just incase, d.js uses Snowflake times instead of our Client's timestamp.
|
|
|
|
if(result && result.size > 0) amount += result.size;
|
|
|
|
} catch(error) {} //eslint-disable-line no-empty
|
|
|
|
|
|
|
|
if(messages.length > 0) {
|
|
|
|
await bulkDelete(messages);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
await bulkDelete(messages.array());
|
|
|
|
return amount;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async filterMessages(messages) {
|
|
|
|
|
|
|
|
const a = this.arguments;
|
|
|
|
|
|
|
|
const filters = {
|
|
|
|
AND: (m) => {
|
|
|
|
return (a.users ? a.users.value.map((u) => u.id).includes(m.author.id) : true)
|
|
|
|
&& (a.bots ? m.author.bot : true)
|
|
|
|
&& (a.text ? m.content.toLowerCase().includes(a.text.value.toLowerCase()) : true)
|
|
|
|
&& (a.startswith ? m.content.toLowerCase().startsWith(a.startswith.value.toLowerCase()) : true)
|
|
|
|
&& (a.endswith ? m.content.toLowerCase().endsWith(a.endswith.value.toLowerCase()) : true)
|
|
|
|
&& (a.emojis ? (/<?(a)?:?(\w{2,32}):(\d{17,19})>?/u).match(m.content) : true) //does not support unicode emojis... might want to support? idk
|
|
|
|
&& (a.reactions ? m.reactions.cache.size > 0 : true)
|
|
|
|
&& (a.images ? m.attachments.some((a) => a.height && a.width) : true)
|
|
|
|
&& (a.attachments ? m.attachments.size > 0 : true);
|
|
|
|
},
|
|
|
|
OR: (m) => {
|
|
|
|
return (a.users ? a.users.value.map((u) => u.id).includes(m.author.id) : false)
|
|
|
|
|| (a.bots ? m.author.bot : false)
|
|
|
|
|| (a.text ? m.content.toLowerCase().includes(a.text.value.toLowerCase()) : false)
|
|
|
|
|| (a.startswith ? m.content.toLowerCase().startsWith(a.startswith.value.toLowerCase()) : false)
|
|
|
|
|| (a.endswith ? m.content.toLowerCase().endsWith(a.endswith.value.toLowerCase()) : false)
|
|
|
|
|| (a.emojis ? (/<?(a)?:?(\w{2,32}):(\d{17,19})>?/u).match(m.content) : false) //does not support unicode emojis... might want to support? idk
|
|
|
|
|| (a.reactions ? m.reactions.cache.size > 0 : false)
|
|
|
|
|| (a.images ? m.attachments.some((a) => a.height && a.width) : false)
|
|
|
|
|| (a.attachments ? m.attachments.size > 0 : false);
|
|
|
|
},
|
|
|
|
NAND: (m) => {
|
|
|
|
return (a.users ? !a.users.value.map((u) => u.id).includes(m.author.id) : true)
|
|
|
|
&& (a.bots ? !m.author.bot : true)
|
|
|
|
&& (a.text ? !m.content.toLowerCase().includes(a.text.value.toLowerCase()) : true)
|
|
|
|
&& (a.startswith ? !m.content.toLowerCase().startsWith(a.startswith.value.toLowerCase()) : true)
|
|
|
|
&& (a.endswith ? !m.content.toLowerCase().endsWith(a.endswith.value.toLowerCase()) : true)
|
|
|
|
&& (a.emojis ? !(/<?(a)?:?(\w{2,32}):(\d{17,19})>?/u).match(m.content) : true) //does not support unicode emojis... might want to support? idk
|
|
|
|
&& (a.reactions ? m.reactions.cache.size === 0 : true)
|
|
|
|
&& (a.images ? !m.attachments.some((a) => a.height && a.width) : true)
|
|
|
|
&& (a.attachments ? m.attachments.size === 0 : true);
|
|
|
|
},
|
|
|
|
NOR: (m) => {
|
|
|
|
return (a.users ? !a.users.value.map((u) => u.id).includes(m.author.id) : false)
|
|
|
|
|| (a.bots ? !m.author.bot : false)
|
|
|
|
|| (a.text ? !m.content.toLowerCase().includes(a.text.value.toLowerCase()) : false)
|
|
|
|
|| (a.startswith ? !m.content.toLowerCase().startsWith(a.startswith.value.toLowerCase()) : false)
|
|
|
|
|| (a.endswith ? !m.content.toLowerCase().endsWith(a.endswith.value.toLowerCase()) : false)
|
|
|
|
|| (a.emojis ? !(/<?(a)?:?(\w{2,32}):(\d{17,19})>?/u).match(m.content) : false) //does not support unicode emojis... might want to support? idk
|
|
|
|
|| (a.reactions ? m.reactions.cache.size === 0 : false)
|
|
|
|
|| (a.images ? !m.attachments.some((a) => a.height && a.width) : false)
|
|
|
|
|| (a.attachments ? m.attachments.size === 0 : false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const type = a.and ? 'AND' : 'OR';
|
|
|
|
const method = a.not ? `N${type}` : type;
|
|
|
|
|
|
|
|
let found = false;
|
|
|
|
for(const arg of Object.keys(a)) {
|
|
|
|
if(Arguments.includes(arg)) found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!found) return messages;
|
|
|
|
|
|
|
|
return messages.filter((m) => {
|
|
|
|
return filters[method](m);
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async fetchMessages(message, amount) {
|
|
|
|
|
|
|
|
let fetched = new Collection();
|
|
|
|
const fetch = async (lastMessage, amount) => {
|
|
|
|
const messages = await this.target.messages.fetch({
|
|
|
|
limit: amount > 100 ? 100 : amount,
|
|
|
|
before: lastMessage,
|
|
|
|
after: this.arguments.after ? this.arguments.after.value : null
|
|
|
|
});
|
|
|
|
|
|
|
|
fetched = fetched.concat(messages);
|
|
|
|
const remaining = amount - 100;
|
|
|
|
if(remaining > 0) {
|
|
|
|
await fetch(messages.lastKey(), remaining);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
await fetch(this.arguments.before ? this.arguments.before.value : message, amount);
|
|
|
|
return fetched;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
description() {
|
|
|
|
return `\n${this.guild.format('INFRACTION_DESCRIPTIONAMOUNT', { amount: this.data.amount })}`;
|
|
|
|
}
|
2020-07-20 00:42:21 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
}
|
2020-07-04 12:23:10 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Prune;
|