2020-07-06 03:15:06 -04:00
|
|
|
/* eslint-disable no-bitwise */
|
|
|
|
import { Member, User } from 'eris';
|
|
|
|
import { randomBytes } from 'crypto';
|
|
|
|
import moment, { unitOfTime } from 'moment';
|
|
|
|
import { Client, RichEmbed } from '.';
|
|
|
|
import { Moderation as ModerationModel, ModerationInterface } from '../models';
|
|
|
|
import { moderation as channels } from '../configs/channels.json';
|
|
|
|
|
|
|
|
export default class Moderation {
|
|
|
|
public client: Client;
|
|
|
|
|
|
|
|
public logChannels: {
|
|
|
|
modlogs: string
|
|
|
|
};
|
|
|
|
|
|
|
|
constructor(client: Client) {
|
|
|
|
this.client = client;
|
|
|
|
this.logChannels = {
|
|
|
|
modlogs: channels.modlogs,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public checkPermissions(member: Member, moderator: Member): boolean {
|
|
|
|
if (member.id === moderator.id) return false;
|
|
|
|
if (member.roles.some((r) => ['662163685439045632', '455972169449734144', '453689940140883988'].includes(r))) return false;
|
|
|
|
const bit = member.permission.allow;
|
|
|
|
if ((bit | 8) === bit) return false;
|
|
|
|
if ((bit | 20) === bit) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts some sort of time based duration to milliseconds based on length.
|
|
|
|
* @param time The time, examples: 2h, 1m, 1w
|
|
|
|
*/
|
|
|
|
public convertTimeDurationToMilliseconds(time: string): number {
|
|
|
|
const lockLength = time.match(/[a-z]+|[^a-z]+/gi);
|
|
|
|
const length = Number(lockLength[0]);
|
|
|
|
const unit = lockLength[1] as unitOfTime.Base;
|
|
|
|
return moment.duration(length, unit).asMilliseconds();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async ban(user: User, moderator: Member, duration: number, reason?: string): Promise<ModerationInterface> {
|
|
|
|
if (reason && reason.length > 512) throw new Error('Ban reason cannot be longer than 512 characters');
|
|
|
|
await this.client.guilds.get(this.client.config.guildID).banMember(user.id, 7, reason);
|
|
|
|
const logID = randomBytes(2).toString('hex');
|
|
|
|
const mod = new ModerationModel({
|
|
|
|
userID: user.id,
|
|
|
|
logID,
|
|
|
|
moderatorID: moderator.id,
|
|
|
|
reason: reason || null,
|
|
|
|
type: 5,
|
|
|
|
date: new Date(),
|
|
|
|
});
|
|
|
|
const now: number = Date.now();
|
|
|
|
let date: Date;
|
|
|
|
let processed = true;
|
|
|
|
if (duration > 0) {
|
|
|
|
date = new Date(now + duration);
|
|
|
|
processed = false;
|
|
|
|
} else date = null;
|
|
|
|
const expiration = { date, processed };
|
|
|
|
mod.expiration = expiration;
|
|
|
|
|
|
|
|
const embed = new RichEmbed();
|
|
|
|
embed.setTitle(`Case ${logID} | Ban`);
|
|
|
|
embed.setColor('#e74c3c');
|
|
|
|
embed.setAuthor(user.username, user.avatarURL);
|
|
|
|
embed.setThumbnail(user.avatarURL);
|
|
|
|
embed.addField('User', `<@${user.id}>`, true);
|
|
|
|
embed.addField('Moderator', `<@${moderator.id}>`, true);
|
|
|
|
if (reason) {
|
|
|
|
embed.addField('Reason', reason, true);
|
|
|
|
}
|
|
|
|
if (date) {
|
|
|
|
embed.addField('Expiration', moment(date).calendar(), true);
|
|
|
|
}
|
|
|
|
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
|
|
|
|
embed.setTimestamp();
|
|
|
|
this.client.createMessage(this.logChannels.modlogs, { embed });
|
|
|
|
return mod.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async unban(userID: string, moderator: Member, reason?: string): Promise<ModerationInterface> {
|
|
|
|
this.client.unbanGuildMember(this.client.config.guildID, userID, reason);
|
|
|
|
const user = await this.client.getRESTUser(userID);
|
|
|
|
if (!user) throw new Error('Cannot get user.');
|
|
|
|
const logID = randomBytes(2).toString('hex');
|
|
|
|
const mod = new ModerationModel({
|
|
|
|
userID,
|
|
|
|
logID,
|
|
|
|
moderatorID: moderator.id,
|
|
|
|
reason: reason || null,
|
|
|
|
type: 4,
|
|
|
|
date: new Date(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const embed = new RichEmbed();
|
|
|
|
embed.setTitle(`Case ${logID} | Unban`);
|
|
|
|
embed.setColor('#1abc9c');
|
|
|
|
embed.setAuthor(user.username, user.avatarURL);
|
|
|
|
embed.setThumbnail(user.avatarURL);
|
|
|
|
embed.addField('User', `<@${user.id}>`, true);
|
|
|
|
embed.addField('Moderator', `<@${moderator.id}>`, true);
|
|
|
|
if (reason) {
|
|
|
|
embed.addField('Reason', reason, true);
|
|
|
|
}
|
|
|
|
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
|
|
|
|
embed.setTimestamp();
|
|
|
|
this.client.createMessage(this.logChannels.modlogs, { embed });
|
|
|
|
return mod.save();
|
|
|
|
}
|
|
|
|
|
2020-07-09 04:41:29 -04:00
|
|
|
public async mute(user: User, moderator: Member, duration: number, reason?: string): Promise<ModerationInterface> {
|
|
|
|
if (reason && reason.length > 512) throw new Error('Mute reason cannot be longer than 512 characters');
|
|
|
|
const member = await this.client.getRESTGuildMember(this.client.config.guildID, user.id);
|
|
|
|
if (!member) throw new Error('Cannot find member.');
|
|
|
|
await member.addRole('478373942638149643', `Muted by ${moderator.username}#${moderator.discriminator}`);
|
|
|
|
const logID = randomBytes(2).toString('hex');
|
|
|
|
const mod = new ModerationModel({
|
|
|
|
userID: user.id,
|
|
|
|
logID,
|
|
|
|
moderatorID: moderator.id,
|
|
|
|
reason: reason || null,
|
|
|
|
type: 2,
|
|
|
|
date: new Date(),
|
|
|
|
});
|
|
|
|
const now: number = Date.now();
|
|
|
|
let date: Date;
|
|
|
|
let processed = true;
|
|
|
|
if (duration > 0) {
|
|
|
|
date = new Date(now + duration);
|
|
|
|
processed = false;
|
|
|
|
} else date = null;
|
|
|
|
const expiration = { date, processed };
|
|
|
|
mod.expiration = expiration;
|
|
|
|
await this.client.db.local.set(`muted-${member.id}`, true);
|
|
|
|
|
|
|
|
const embed = new RichEmbed();
|
|
|
|
embed.setTitle(`Case ${logID} | Mute`);
|
|
|
|
embed.setColor('#ffff00');
|
|
|
|
embed.setAuthor(user.username, user.avatarURL);
|
|
|
|
embed.setThumbnail(user.avatarURL);
|
|
|
|
embed.addField('User', `<@${user.id}>`, true);
|
|
|
|
embed.addField('Moderator', `<@${moderator.id}>`, true);
|
|
|
|
if (reason) {
|
|
|
|
embed.addField('Reason', reason, true);
|
|
|
|
}
|
|
|
|
if (date) {
|
|
|
|
embed.addField('Expiration', moment(date).calendar(), true);
|
|
|
|
}
|
|
|
|
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
|
|
|
|
embed.setTimestamp();
|
|
|
|
this.client.createMessage(this.logChannels.modlogs, { embed });
|
|
|
|
return mod.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async unmute(userID: string, moderator: Member, reason?: string): Promise<ModerationInterface> {
|
|
|
|
const member = await this.client.getRESTGuildMember(this.client.config.guildID, userID);
|
|
|
|
if (!member) {
|
|
|
|
await this.client.db.local.del(`muted-${userID}`);
|
|
|
|
throw new Error('Member doesn\'t exist.');
|
|
|
|
}
|
|
|
|
await member.removeRole('478373942638149643');
|
|
|
|
const logID = randomBytes(2).toString('hex');
|
|
|
|
const mod = new ModerationModel({
|
|
|
|
userID,
|
|
|
|
logID,
|
|
|
|
moderatorID: moderator.id,
|
|
|
|
reason: reason || null,
|
|
|
|
type: 1,
|
|
|
|
date: new Date(),
|
|
|
|
});
|
|
|
|
|
|
|
|
await this.client.db.local.del(`muted-${member.id}`);
|
|
|
|
|
|
|
|
const embed = new RichEmbed();
|
|
|
|
embed.setTitle(`Case ${logID} | Unmute`);
|
|
|
|
embed.setColor('#1abc9c');
|
|
|
|
embed.setAuthor(member.user.username, member.user.avatarURL);
|
|
|
|
embed.setThumbnail(member.user.avatarURL);
|
|
|
|
embed.addField('User', `<@${member.user.id}>`, true);
|
|
|
|
embed.addField('Moderator', `<@${moderator.id}>`, true);
|
|
|
|
if (reason) {
|
|
|
|
embed.addField('Reason', reason, true);
|
|
|
|
}
|
|
|
|
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
|
|
|
|
embed.setTimestamp();
|
|
|
|
this.client.createMessage(this.logChannels.modlogs, { embed });
|
|
|
|
return mod.save();
|
|
|
|
}
|
|
|
|
|
2020-07-06 03:15:06 -04:00
|
|
|
public async kick(user: Member|User, moderator: Member, reason?: string): Promise<ModerationInterface> {
|
|
|
|
if (reason && reason.length > 512) throw new Error('Kick reason cannot be longer than 512 characters');
|
|
|
|
await this.client.guilds.get(this.client.config.guildID).kickMember(user.id, reason);
|
|
|
|
const logID = randomBytes(2).toString('hex');
|
|
|
|
const mod = new ModerationModel({
|
|
|
|
userID: user.id,
|
|
|
|
logID,
|
|
|
|
moderatorID: moderator.id,
|
|
|
|
reason: reason || null,
|
|
|
|
type: 5,
|
|
|
|
date: new Date(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const embed = new RichEmbed();
|
|
|
|
embed.setTitle(`Case ${logID} | Kick`);
|
|
|
|
embed.setColor('#e74c3c');
|
|
|
|
embed.setAuthor(user.username, user.avatarURL);
|
|
|
|
embed.setThumbnail(user.avatarURL);
|
|
|
|
embed.addField('User', `<@${user.id}>`, true);
|
|
|
|
embed.addField('Moderator', `<@${moderator.id}>`, true);
|
|
|
|
if (reason) {
|
|
|
|
embed.addField('Reason', reason, true);
|
|
|
|
}
|
|
|
|
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
|
|
|
|
embed.setTimestamp();
|
|
|
|
this.client.createMessage(this.logChannels.modlogs, { embed });
|
|
|
|
return mod.save();
|
|
|
|
}
|
|
|
|
}
|