diff --git a/cmds/moderation/ban.js b/cmds/moderation/ban.js index 6d2e3cf..08d52a4 100644 --- a/cmds/moderation/ban.js +++ b/cmds/moderation/ban.js @@ -1,9 +1,10 @@ 'use strict'; const commando = require("@iceprod/discord.js-commando"); -const { Message } = require("discord.js"); +const { Message, User } = require("discord.js"); const { DateTime } = require("luxon"); -const { parseDoubleDash, trySend } = require("../../resources/functions"); +const { parseDoubleDash, trySend, errLog, defaultEventLogEmbed, defaultDateFormat, parseComa } = require("../../resources/functions"); +const createInfraction = require("./src/createInfraction"); const { duration, CHECK_FOR_DURATION_REGEXP } = require("./src/duration"); const targetUser = require("./src/targetUser"); @@ -28,11 +29,12 @@ module.exports = class ban extends commando.Command { * @returns */ async run(msg, arg) { + if (!msg.guild.DB) await msg.guild.dbLoad(); const args = parseDoubleDash(arg), - target = args?.shift(); + target = parseComa(args?.shift()); let reason = "No reason provided", pDuration = {}, execTarget = [], resultMsg = "", daysToDeleteMessages = 0; - if (!target || target.length < 1) return trySend(msg.client, msg, this.description); else { + if (!target || target.length < 1) return trySend(msg.client, msg, this.details); else { const ET = await targetUser(msg, target); execTarget = ET.targetUser; resultMsg = ET.resultMsg; @@ -54,9 +56,57 @@ module.exports = class ban extends commando.Command { if (!pDuration.invoked) pDuration.invoked = DateTime.fromJSDate(msg.editedAt || msg.createdAt); if (execTarget.length > 0) { + /** + * @type {User} + */ for (const U of execTarget) { + const INFRACTION = createInfraction(msg, execTarget, "ban", reason), + data = { + duration: pDuration, + infraction: INFRACTION.infraction, + moderator: msg.member + }; + let banned = [], already = [], cant = []; + try { + await U.ban(msg.guild, data, { days: daysToDeleteMessages, reason: reason }); + banned.push(U.id); + } catch (e) { + errLog(e, msg, msg.client, true, "", true); + if (/Missing Permissions|someone with higher position/.test(e.message)) cant.push(U.id); + } + INFRACTION.executed = banned; + INFRACTION.aborted = already; + INFRACTION.failed = cant; + + + const emb = defaultEventLogEmbed(msg.guild) + .setTitle("Infraction #" + INFRACTION.infraction) + .setDescription(reason); + + if (banned.length > 0) { + let bannedStr = "", bannedArr = []; + await msg.guild.addInfraction(INFRACTION); + for (const U of banned) { + const tU = "<@" + U + ">, "; + if ((bannedStr + tU).length < 1000) bannedStr += tU; else bannedArr.push(U); + } + bannedStr = bannedStr.slice(0, -2); + + if (bannedArr.length > 0) bannedStr += ` and ${bannedArr.length} more...`; + if (already.length > 0) emb.addField("Already banned", "<@" + already.join(">, <@") + ">\n\nDuration updated for these users"); + + emb.addField("Banned", bannedStr || "`[NONE]`") + .addField("At", defaultDateFormat(pDuration.invoked), true) + .addField("Until", pDuration.until ? defaultDateFormat(pDuration.until) : "Never", true) + } + emb.addField("For", pDuration.duration?.strings.join(" ") || "Indefinite"); + + if (cant.length > 0) emb.addField("Can't ban", "<@" + cant.join(">, <@") + ">\n\n**You can't ban someone with higher position than you <:nekokekLife:852865942530949160>**") + + return trySend(msg.client, msg, { content: resultMsg, embed: emb }); } + return trySend(msg.client, msg, resultMsg); } } } \ No newline at end of file diff --git a/cmds/moderation/eventlog.js b/cmds/moderation/eventlog.js index ddeab6f..84e5df4 100644 --- a/cmds/moderation/eventlog.js +++ b/cmds/moderation/eventlog.js @@ -239,8 +239,7 @@ module.exports = class eventlog extends commando.Command { return trySend(this.client, msg, (await resultEmbed(this)).setDescription(report.slice(0, 2048))); async function resultEmbed(the) { - const emb = defaultImageEmbed(msg, null, "Event Log Channels Configuration"); - emb + const emb = defaultImageEmbed(msg, null, "Event Log Channels Configuration") .setDescription("`--h` for help") .addField(`Message Edit`, eventChannels?.mesEd?.channel ? `<#${eventChannels?.mesEd.channel}>\n**Ignores:** ${eventChannels?.mesEd?.ignore?.length > 0 ? "<#" + eventChannels?.mesEd.ignore.join(">, <#") + ">" : "None"}` diff --git a/cmds/moderation/mute.js b/cmds/moderation/mute.js index 152370a..65b288a 100644 --- a/cmds/moderation/mute.js +++ b/cmds/moderation/mute.js @@ -127,22 +127,22 @@ module.exports = class mute extends commando.Command { infractionToDoc.aborted = already; infractionToDoc.failed = cant; - if (muted.length > 0) await msg.guild.addInfraction(infractionToDoc); - const emb = defaultEventLogEmbed(msg.guild); - let mutedStr = "", mutedArr = []; - - if (muted.length > 0) for (const U of muted) { - const tU = "<@" + U + ">, "; - if ((mutedStr + tU).length < 1000) mutedStr += tU; else mutedArr.push(U); - } - - if (mutedArr.length > 0) mutedStr += `and ${mutedArr.length} more...`; - if (already.length > 0) emb.addField("Already muted", "<@" + already.join(">, <@") + ">\n\nDuration updated for these users"); - - emb.setTitle("Infraction #" + infractionToDoc.infraction) - .setDescription("**Reason**\n" + reason); + const emb = defaultEventLogEmbed(msg.guild) + .setTitle("Infraction #" + infractionToDoc.infraction) + .setDescription(reason); if (muted.length > 0) { + let mutedStr = "", mutedArr = []; + await msg.guild.addInfraction(infractionToDoc); + for (const U of muted) { + const tU = "<@" + U + ">, "; + if ((mutedStr + tU).length < 1000) mutedStr += tU; else mutedArr.push(U); + } + mutedStr = mutedStr.slice(0, -2); + + if (mutedArr.length > 0) mutedStr += ` and ${mutedArr.length} more...`; + if (already.length > 0) emb.addField("Already muted", "<@" + already.join(">, <@") + ">\n\nDuration updated for these users"); + emb.addField("Muted", mutedStr || "`[NONE]`") .addField("At", defaultDateFormat(duration.invoked), true) .addField("Until", duration.until ? defaultDateFormat(duration.until) : "Never", true) diff --git a/cmds/moderation/src/duration.js b/cmds/moderation/src/duration.js index 6ec062f..f385e45 100644 --- a/cmds/moderation/src/duration.js +++ b/cmds/moderation/src/duration.js @@ -42,11 +42,9 @@ function duration(base, string) { DURATION_ARGS = string.match(DURATION_REGEXP); let changed = false; - console.log(DURATION_ARGS, DT_INVOKED.toFormat(DT_PRINT_FORMAT)); for (const value of DURATION_ARGS) { const val = parseInt(value.match(/[\-]?\d+/)[0], 10); - console.log(val); if (!val) continue; if (value.endsWith("h") || value.endsWith("ho")) { DURATION.hour = DURATION.hour + val; diff --git a/resources/eventsLogger/guildBanAdd.js b/resources/eventsLogger/guildBanAdd.js index b1be3e4..b775c8d 100644 --- a/resources/eventsLogger/guildBanAdd.js +++ b/resources/eventsLogger/guildBanAdd.js @@ -5,23 +5,24 @@ const { getChannel, defaultEventLogEmbed, trySend } = require("../functions"); const getColor = require("../getColor"); /** - * @param {Guild} GUILD - * @param {User} USER - * @returns + * @param {Guild} GUILD + * @param {User} USER + * @returns */ module.exports = async (GUILD, USER) => { - if (GUILD.DB.settings.eventChannels?.ban) { + if (GUILD.DB.eventChannels?.ban) { if (USER.partial) USER = await USER.fetch(); - const log = getChannel(GUILD, GUILD.DB.settings.eventChannels.ban); + const log = getChannel(GUILD, GUILD.DB.eventChannels.ban); if (!log) return; const emb = defaultEventLogEmbed(GUILD); let audit; if (GUILD.member(GUILD.client.user).hasPermission("VIEW_AUDIT_LOG")) { - audit = (await GUILD.fetchAuditLogs({ limit: 1, type: "MEMBER_BAN_ADD" })).entries.first().executor; - } - const rea = (await GUILD.fetchBan(USER)).reason; - emb.setDescription(rea || "No reason provided") - .setTitle(`\`${USER.tag}\` banned` + (audit ? ` by \`${audit.tag}\`` : "")) + audit = (await GUILD.fetchAuditLogs({ limit: 1, type: "MEMBER_BAN_ADD" })).entries.first(); + console.log(audit); + emb.setDescription(audit?.reason || "No reason provided"); + } else emb.setDescription("Unknown reason"); + + emb.setTitle(`\`${USER.tag}\` banned` + (audit?.executor ? ` by \`${audit.executor.tag}\`` : "")) .setColor(getColor("red")) .setThumbnail(USER.displayAvatarURL({ size: 4096, format: "png", dynamic: true })) .addField("User", `<@${USER.id}>\n(${USER.id})`); diff --git a/resources/eventsLogger/guildMemberUpdate.js b/resources/eventsLogger/guildMemberUpdate.js index 69720d5..1d0392e 100644 --- a/resources/eventsLogger/guildMemberUpdate.js +++ b/resources/eventsLogger/guildMemberUpdate.js @@ -21,11 +21,12 @@ module.exports = async (memberold, membernew) => { const emb = defaultEventLogEmbed(membernew.guild), oldT = memberold.toJSON().displayAvatarURL; const oldAV = membernew.user.DB.cachedAvatarURL || oldT; if (oldAV) thumbMes += "This embed's thumbnail is the user's old avatar.\n"; - let audit; + let audit, auditPerm; if (membernew.guild.DB.eventChannels?.memberRole) { log = getChannel(membernew, membernew.guild.DB.eventChannels.memberRole); if (membernew.guild.member(membernew.client.user).hasPermission("VIEW_AUDIT_LOG")) { - audit = (await membernew.guild.fetchAuditLogs({ limit: 1, type: "MEMBER_ROLE_UPDATE" })).entries.first().executor; + audit = (await membernew.guild.fetchAuditLogs({ limit: 1, type: "MEMBER_ROLE_UPDATE" })).entries.first(); + auditPerm = true; } if (membernew.roles.cache.size > memberold.roles.cache.size) { const use = membernew.roles.cache.difference(memberold.roles.cache).sort((a, b) => b.position - a.position).map(r => r.id); @@ -35,7 +36,7 @@ module.exports = async (memberold, membernew) => { ("<@&" + use.slice(0, 39).join(">, <@&") + ">" + (use.length > 39 ? ` and ${use.slice(39).length} more...` : ""))) .setDescription(`**Old role${use2.length > 2 ? "s" : ""}**\n` + (memberold.roles.cache.size > 1 ? "<@&" + - use2.slice(0, 82).join(">, <@&") + ">" + (use2.length > 82 ? ` and ${use2.slice(82).length} more...` : "") : "`[NONE]`")); + use2.slice(0, 80).join(">, <@&") + ">" + (use2.length > 80 ? ` and ${use2.slice(80).length} more...` : "") : "`[NONE]`")); } if (membernew.roles.cache.size < memberold.roles.cache.size) { const use = memberold.roles.cache.difference(membernew.roles.cache).sort((a, b) => b.position - a.position).map(r => r.id); @@ -45,14 +46,15 @@ module.exports = async (memberold, membernew) => { ("<@&" + use.slice(0, 39).join(">, <@&") + ">" + (use.length > 39 ? ` and ${use.slice(39).length} more...` : ""))) .setDescription(`**Current role${membernew.roles.cache.size > 2 ? "s" : ""}**\n` + (membernew.roles.cache.size > 1 ? "<@&" + - use2.slice(0, 82).join(">, <@&") + ">" + (use2.length > 82 ? ` and ${use2.slice(82).length} more...` : "") : "`[NONE]`")); + use2.slice(0, 80).join(">, <@&") + ">" + (use2.length > 80 ? ` and ${use2.slice(80).length} more...` : "") : "`[NONE]`")); } } if (membernew.guild.DB.eventChannels?.member && membernew.roles.cache.size === memberold.roles.cache.size) { log = getChannel(membernew, membernew.guild.DB.eventChannels.member); if (membernew.displayName !== memberold.displayName) { if (membernew.guild.member(membernew.client.user).hasPermission("VIEW_AUDIT_LOG")) { - audit = (await membernew.guild.fetchAuditLogs({ limit: 1, type: "MEMBER_UPDATE" })).entries.first().executor; + audit = (await membernew.guild.fetchAuditLogs({ limit: 1, type: "MEMBER_UPDATE" })).entries.first(); + auditPerm = true; } emb.addField("Current Nickname", "`" + membernew.displayName + "`") .addField("Original Nickname", "`" + memberold.displayName + "`") @@ -66,8 +68,11 @@ module.exports = async (memberold, membernew) => { } emb.setAuthor(emb.author.name, NEWAV) .setTitle("Profile `" + memberold.user.tag + "` updated" + - (audit ? ` by \`${audit.tag}\`` : "")) + (audit?.executor ? ` by \`${audit.executor.tag}\`` : "")) .setColor(getColor("blue")); + if (auditPerm) { + emb.setDescription((audit?.reason || "No reason provided") + (emb.description ? "\n\n" + emb.description : "")); + } else emb.setDescription("Unknown reason\n\n" + emb.description); membernew.user.DB.cachedAvatarURL = NEWAV; membernew.user.setDb("cachedAvatarURL", membernew.user.DB.cachedAvatarURL); if (!emb.fields || emb.fields.length === 0) return; diff --git a/resources/eventsLogger/messageUpdate.js b/resources/eventsLogger/messageUpdate.js index 1613220..185945b 100644 --- a/resources/eventsLogger/messageUpdate.js +++ b/resources/eventsLogger/messageUpdate.js @@ -20,8 +20,7 @@ module.exports = async (msgold, msgnew) => { if (msgnew.guild.DB.eventChannels.mesEd?.channel !== msgnew.channel.id && ignored === false || check) { const log = getChannel(msgnew, msgnew.guild.DB.eventChannels.mesEd?.channel); if (!log || !msgnew.author) return; - const emb = defaultEventLogEmbed(msgnew.guild); - emb + const emb = defaultEventLogEmbed(msgnew.guild) .setColor(getColor("blue")) .setDescription(msgnew.content.length > 0 ? msgnew.content : "`[EMPTY]`") .addField("Original content", msgold.content?.length > 0 ? (msgold.content.slice(0, msgold.content.length < 1025 ? 1024 : 1021) + (msgold.content.length < 1025 ? "" : "...")) : "`[EMPTY]`") diff --git a/resources/structures.js b/resources/structures.js index 545b60c..9231b71 100644 --- a/resources/structures.js +++ b/resources/structures.js @@ -1,6 +1,6 @@ 'use strict'; -const { Structures, Guild, GuildMember, BanOptions } = require("discord.js"), +const { Structures, Guild, GuildMember, BanOptions, Role } = require("discord.js"), { database } = require("../database/mongo"), { errLog, defaultEventLogEmbed, defaultDateFormat, trySend } = require("./functions"); const { createSchedule } = require("../cmds/moderation/src/createSchedule"); @@ -50,7 +50,7 @@ Structures.extend("Guild", u => { * @param {string} userID - User ID * @returns {object[]} Array of infractions objects */ - async getInfractions(userID) { + getInfractions(userID) { let ret = [] for (const [k, v] of this.DB.infractions) if (v.by.map(r => r.id).includes(userID)) ret.push(v); @@ -194,7 +194,7 @@ Structures.extend("User", u => { */ async mute(guild, data, reason) { if (!guild || !(guild instanceof Guild)) throw new TypeError("Guild is: " + guild); - if (!data?.infraction) throw new Error("Missing infraction id"); + if (!data || !data.infraction) throw new Error("Missing infraction id"); const MEM = guild.member(this); const CL = guild.member(this.client.user); @@ -269,17 +269,16 @@ Structures.extend("User", u => { */ async ban(guild, data, option) { if (!guild || !(guild instanceof Guild)) throw new TypeError("Guild is: " + guild); - if (!data?.infraction) throw new Error("Missing infraction id"); + if (!data || !data.infraction) throw new Error("Missing infraction id"); const MEM = guild.member(this); const CL = guild.member(this.client.user); if (!(CL.isAdmin || CL.hasPermission("BAN_MEMBERS")) || - !(data.moderation.isAdmin || data.moderator.hasPermission("BAN_MEMBERS"))) throw new Error("Missing Permissions"); + !(data.moderator.isAdmin || data.moderator.hasPermission("BAN_MEMBERS"))) throw new Error("Missing Permissions"); if (MEM) { - if (moderator.roles.highest.position < MEM.roles.highest.position || + if (data.moderator.roles.highest.position < MEM.roles.highest.position || MEM.roles.highest.position > CL.roles.highest.position) - throw new Error("You can't mute someone with higher position than you <:nekokekLife:852865942530949160>"); + throw new Error("You can't ban someone with higher position than you <:nekokekLife:852865942530949160>"); } - await guild.members.ban(this, option); if (!guild.DB) await guild.dbLoad(); if (!this.bot) { @@ -289,8 +288,9 @@ Structures.extend("User", u => { .addField("At", defaultDateFormat(data.duration.invoked), true) .addField("Until", data.duration.until ? defaultDateFormat(data.duration.until) : "Never", true) .addField("For", data.duration.duration?.strings.join(" ") || "Indefinite"); - this.createDM().then(r => trySend(this.client, r, emb)); + await this.createDM().then(r => trySend(this.client, r, emb)); } + await guild.members.ban(this, option); const MC = guild.getTimedPunishment(this.id, "ban"), TP = new TimedPunishment({ userID: this.id, duration: data.duration, infraction: data.infraction, type: "ban" }); @@ -312,11 +312,9 @@ Structures.extend("User", u => { emb.setTitle("You have been unbanned") .setDescription("**Reason**\n" + reason); - this.createDM().then(r => trySend(this.client, r, emb)); } await col.deleteOne({ document: [guild.id, this.id, "ban"].join("/") }).then(() => console.log("SCHEDULE " + [guild.id, this.id, "ban"].join("/") + " DELETED")).catch(e => errLog(e, null, client)); - return guild.removeTimedPunishment(this.id, "ban"); } } @@ -374,7 +372,7 @@ Structures.extend("GuildMember", u => { } async dbLoad() { - return database.collection("GuildMember").findOne({ document: this.id }).then((r, e) => { + return database.collection("GuildMember").findOne({ document: this.guild.id + "/" + this.id }).then((r, e) => { if (e) return errLog(e, null, this.client); if (!r) r = {}; return this.DB = r; @@ -382,14 +380,14 @@ Structures.extend("GuildMember", u => { } async setDb(query, set) { - return database.collection("GuildMember").updateOne({ document: this.id }, { $set: { [query]: set }, $setOnInsert: { document: this.id } }, + return database.collection("GuildMember").updateOne({ document: this.guild.id + "/" + this.id }, { $set: { [query]: set }, $setOnInsert: { document: this.id } }, { upsert: true }).then((r, e) => { if (e) return errLog(e, null, this.client); return this.DB[query] = set; }); } - async infractions() { + get infractions() { return this.guild.getInfractions(this.id); } @@ -442,6 +440,13 @@ Structures.extend("GuildMember", u => { } } + /** + * @param {string[]} roles + */ + async setLeaveRoles(roles = []) { + return this.setDb("leaveRoles", roles); + } + get isAdmin() { if (!this.client.owners.includes(this.user)) return this.hasPermission("ADMINISTRATOR"); else return true } } }); \ No newline at end of file