2023-02-24 19:55:11 -05:00
|
|
|
import Discord from 'discord.js';
|
2023-04-14 06:47:58 -04:00
|
|
|
import TClient from '../client.js';
|
2023-02-24 19:55:11 -05:00
|
|
|
import mongoose from 'mongoose';
|
|
|
|
import ms from 'ms';
|
2023-04-14 06:47:58 -04:00
|
|
|
import {Punishment} from '../typings/interfaces.js';
|
2023-02-24 19:55:11 -05:00
|
|
|
|
|
|
|
const Schema = mongoose.model('punishments', new mongoose.Schema({
|
|
|
|
_id: {type: Number, required: true},
|
|
|
|
type: {type: String, required: true},
|
|
|
|
member: {type: String, required: true},
|
|
|
|
moderator: {type: String, required: true},
|
|
|
|
expired: {type: Boolean},
|
|
|
|
time: {type: Number, required: true},
|
|
|
|
reason: {type: String, required: true},
|
|
|
|
endTime: {type: Number},
|
|
|
|
cancels: {type: Number},
|
|
|
|
duration: {type: Number}
|
2023-02-27 02:14:54 -05:00
|
|
|
}, {versionKey: false}));
|
2023-02-24 19:55:11 -05:00
|
|
|
|
|
|
|
export default class punishments extends Schema {
|
|
|
|
client: TClient;
|
|
|
|
_content: typeof Schema;
|
|
|
|
constructor(client:TClient){
|
|
|
|
super();
|
|
|
|
this.client = client;
|
|
|
|
this._content = Schema;
|
|
|
|
}
|
|
|
|
createId = async()=>Math.max(...(await this._content.find({})).map(x=>x.id), 0) + 1;
|
|
|
|
async makeModlogEntry(punishment:Punishment){
|
|
|
|
// Format data into an embed
|
2023-04-29 19:41:12 -04:00
|
|
|
const channel = ['kick', 'ban'].includes(punishment.type) ? this.client.config.mainServer.channels.bankick_log : this.client.config.mainServer.channels.logs;
|
|
|
|
const embed = new this.client.embed().setTitle(`${punishment.type[0].toUpperCase() + punishment.type.slice(1)} | Case #${punishment._id}`)
|
2023-02-24 19:55:11 -05:00
|
|
|
.addFields(
|
|
|
|
{name: '🔹 User', value: `<@${punishment.member}>\n\`${punishment.member}\``, inline: true},
|
|
|
|
{name: '🔹 Moderator', value: `<@${punishment.moderator}>\n\`${punishment.moderator}\``, inline: true},
|
|
|
|
{name: '\u200b', value: '\u200b', inline: true},
|
|
|
|
{name: '🔹 Reason', value: `\`${punishment.reason}\``, inline: true})
|
2023-03-18 04:19:41 -04:00
|
|
|
.setColor(this.client.config.embedColor).setTimestamp(punishment.time)
|
2023-02-24 19:55:11 -05:00
|
|
|
if (punishment.duration) embed.addFields({name: '🔹 Duration', value: this.client.formatTime(punishment.duration, 100), inline: true}, {name: '\u200b', value: '\u200b', inline: true})
|
|
|
|
if (punishment.cancels) {
|
|
|
|
const cancels = await this._content.findById(punishment.cancels);
|
|
|
|
embed.addFields({name: '🔹 Overwrites', value: `This case overwrites Case #${cancels.id}\n\`${cancels.reason}\``})
|
|
|
|
}
|
|
|
|
// Send it off to specific Discord channel.
|
2023-02-26 14:19:02 -05:00
|
|
|
(this.client.channels.cache.get(channel) as Discord.TextChannel).send({embeds:[embed]});
|
2023-05-07 04:41:20 -04:00
|
|
|
}
|
2023-02-24 19:55:11 -05:00
|
|
|
getTense(type:string){// Get past tense form of punishment type, grammar yes
|
|
|
|
return {
|
|
|
|
ban: 'banned',
|
|
|
|
softban: 'softbanned',
|
|
|
|
kick: 'kicked',
|
|
|
|
mute: 'muted',
|
|
|
|
warn: 'warned'
|
|
|
|
}[type]
|
|
|
|
}
|
|
|
|
async addPunishment(type:string, options:{time?:string,interaction?:Discord.ChatInputCommandInteraction<'cached'>},moderator:string,reason:string,User:Discord.User,GuildMember?:Discord.GuildMember){
|
|
|
|
const {time,interaction} = options;
|
|
|
|
const now = Date.now();
|
|
|
|
const guild = this.client.guilds.cache.get(this.client.config.mainServer.id) as Discord.Guild;
|
|
|
|
const punData:Punishment={type, _id: await this.createId(), member:User.id, reason, moderator, time:now}
|
|
|
|
const inOrFromBoolean = ['warn', 'mute'].includes(type) ? 'in' : 'from';
|
|
|
|
const auditLogReason = `${reason || 'Reason unspecified'} | Case #${punData._id}`;
|
|
|
|
const embed = new this.client.embed()
|
|
|
|
.setColor(this.client.config.embedColor)
|
|
|
|
.setTitle(`Case #${punData._id}: ${type[0].toUpperCase()+type.slice(1)}`)
|
|
|
|
.setDescription(`${User.tag}\n<@${User.id}>\n(\`${User.id}\`)`)
|
|
|
|
.addFields({name: 'Reason', value: reason})
|
|
|
|
let punResult;
|
|
|
|
let timeInMillis;
|
|
|
|
let DM;
|
|
|
|
|
|
|
|
if (type == 'mute') timeInMillis = time ? ms(time) : 2419140000; // Timeouts have a limit of 4 weeks
|
|
|
|
else timeInMillis = time ? ms(time) : null;
|
|
|
|
|
|
|
|
const durationText = timeInMillis ? ` for ${this.client.formatTime(timeInMillis, 4, {longNames:true,commas:true})}` : '';
|
|
|
|
if (time) embed.addFields({name: 'Duration', value: durationText});
|
|
|
|
|
|
|
|
if (GuildMember){
|
|
|
|
try{
|
|
|
|
DM=await GuildMember.send(`You've been ${this.getTense(type)} ${inOrFromBoolean} ${guild.name}${durationText} for \`${reason}\` (Case #${punData._id})`);
|
|
|
|
}catch(err){
|
|
|
|
embed.setFooter({text: 'Failed to DM a member.'})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (['ban', 'softban'].includes(type)){
|
|
|
|
const banned = await guild.bans.fetch(User.id).catch(()=>undefined);
|
|
|
|
if (!banned) punResult = await guild.bans.create(User.id, {reason: auditLogReason, deleteMessageSeconds: 172800}).catch((err:Error)=>err.message)
|
|
|
|
else punResult = 'User is already banned.';
|
|
|
|
}
|
|
|
|
else if (type == 'kick') punResult = await GuildMember?.kick(auditLogReason).catch((err:Error)=>err.message);
|
|
|
|
else if (type == 'mute') punResult = await GuildMember?.timeout(timeInMillis, auditLogReason).catch((err:Error)=>err.message);
|
|
|
|
if (type == 'softban' && typeof punResult != 'string') punResult = await guild.bans.remove(User.id, auditLogReason).catch((err:Error)=>err.message);
|
|
|
|
|
|
|
|
if (timeInMillis && ['mute','ban'].includes(type)){
|
|
|
|
punData.endTime = now + timeInMillis;
|
|
|
|
punData.duration = timeInMillis;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof punResult == 'string'){// Unsuccessful punishment
|
|
|
|
if (DM) DM.delete();
|
|
|
|
if (interaction) return interaction.editReply(punResult);
|
|
|
|
else return punResult;
|
|
|
|
} else {
|
|
|
|
await this.makeModlogEntry(punData);
|
|
|
|
await this._content.create(punData);
|
|
|
|
|
|
|
|
if (interaction) return interaction.editReply({embeds:[embed]});
|
|
|
|
else return punResult;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
async removePunishment(caseId:number,moderator:string,reason:string,interaction?:Discord.ChatInputCommandInteraction<'cached'>){
|
|
|
|
const now = Date.now();
|
|
|
|
const _id = await this.createId();
|
|
|
|
const punishment = await this._content.findById(caseId);
|
|
|
|
if (!punishment) return 'Punishment not found.';
|
|
|
|
const guild = this.client.guilds.cache.get(this.client.config.mainServer.id) as Discord.Guild;
|
|
|
|
const auditLogReason = `${reason || 'Reason unspecified'} | Case #${punishment.id}`;
|
|
|
|
const User = await this.client.users.fetch(punishment.member);
|
2023-05-07 04:41:20 -04:00
|
|
|
const GuildMember = await guild.members.fetch(punishment.member).catch(()=>null);
|
2023-02-24 19:55:11 -05:00
|
|
|
|
|
|
|
let removePunishmentData:Punishment={type:`un${punishment.type}`, _id, cancels:punishment.id, member:punishment.member, reason, moderator, time:now};
|
|
|
|
let removePunishmentResult;
|
|
|
|
|
|
|
|
if (punishment.type == 'ban') removePunishmentResult = guild.bans.remove(punishment.member, auditLogReason).catch((err:Error)=>err.message);
|
|
|
|
else if (punishment.type == 'mute'){
|
|
|
|
if (GuildMember){
|
|
|
|
removePunishmentResult = GuildMember.timeout(null, auditLogReason).catch((err:Error)=>err.message);
|
|
|
|
GuildMember.send(`You've been unmuted in ${guild.name}.`).catch((err:Error)=>console.log(err.message));
|
|
|
|
} else await this._content.findByIdAndUpdate(caseId,{expired:true},{new:true});
|
2023-04-29 19:08:21 -04:00
|
|
|
} else removePunishmentData.type = 'punishmentOverride';
|
2023-02-24 19:55:11 -05:00
|
|
|
|
|
|
|
if (typeof removePunishmentResult == 'string'){//Unsuccessful punishment
|
|
|
|
if (interaction) return interaction.reply(removePunishmentResult);
|
|
|
|
else return removePunishmentResult;
|
|
|
|
} else {
|
|
|
|
await this._content.findByIdAndUpdate(caseId,{expired:true},{new:true});
|
|
|
|
await this._content.create(removePunishmentData);
|
|
|
|
await this.makeModlogEntry(removePunishmentData);
|
|
|
|
|
2023-04-29 19:41:12 -04:00
|
|
|
if (interaction) return interaction.reply({embeds:[new this.client.embed().setColor(this.client.config.embedColor)
|
|
|
|
.setTitle(`Case #${removePunishmentData._id}: ${removePunishmentData.type[0].toUpperCase()+removePunishmentData.type.slice(1)}`)
|
|
|
|
.setDescription(`${User.tag}\n<@${User.id}>\n(\`${User.id}\`)`)
|
|
|
|
.addFields({name: 'Reason', value: reason},{name: 'Overwrites', value: `Case #${punishment.id}`})
|
|
|
|
]});
|
|
|
|
else return `Successfully un${this.getTense(removePunishmentData.type.replace('un', ''))} ${User.tag} (\`${User.id}\`) for ${reason}`
|
2023-02-24 19:55:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|