From 2bd1774e4fd94c34f873357e49be56df1ec324d3 Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Fri, 23 Jul 2021 14:30:10 +0700 Subject: [PATCH] Plan: Move user punishments to guild --- cmds/moderation/ban.js | 63 +++++++++++++++++++++ cmds/moderation/mute.js | 32 ++++------- cmds/moderation/src/createInfraction.js | 21 +++++++ cmds/moderation/src/duration.js | 5 +- cmds/moderation/src/targetUser.js | 2 +- cmds/owner/unknown-command.js | 2 +- cmds/profile/avatar.js | 2 +- cmds/profile/profile.js | 12 ++-- cmds/profile/servav.js | 14 ++--- cmds/utility/setfootq.js | 13 ++--- resources/eventsLogger/guildMemberAdd.js | 2 +- resources/eventsLogger/guildMemberRemove.js | 5 +- resources/structures.js | 28 +++++---- 13 files changed, 135 insertions(+), 66 deletions(-) create mode 100644 cmds/moderation/ban.js create mode 100644 cmds/moderation/src/createInfraction.js diff --git a/cmds/moderation/ban.js b/cmds/moderation/ban.js new file mode 100644 index 0000000..5d08464 --- /dev/null +++ b/cmds/moderation/ban.js @@ -0,0 +1,63 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { Message } = require("discord.js"); +const { DateTime } = require("luxon"); +const { parseDoubleDash, trySend } = require("../../resources/functions"); +const { duration, CHECK_FOR_DURATION_REGEXP } = require("./src/duration"); +const targetUser = require("./src/targetUser"); + +module.exports = class ban extends commando.Command { + constructor(client) { + super(client, { + name: "ban", + memberName: "ban", + group: "moderation", + description: "Ban pesky troll and toxic members", + guildOnly: true, + userPermissions: ["BAN_MEMBERS"], + clientPermissions: ["BAN_MEMBERS"], + details: "Args: `user_[name|mention|ID] -- [reason] -- [duration] [--d [number of days to delete messages of the user to ban]]`", + examples: ["some user name, some user tag -- 10d76y8m99mo6h70w -- sending unsolicited cakes in DM --d 5"] + }); + } + + /** + * + * @param {Message} msg + * @param {*} arg + * @returns + */ + async run(msg, arg) { + const args = parseDoubleDash(arg), + target = args?.shift(); + let reason = "No reason provided", pDuration = {}, execTarget = [], resultMsg = "", daysToDeleteMessage = 0; + + if (!target || target.length < 1) return trySend(msg.client, msg, this.description); else { + const ET = await targetUser(msg, target); + execTarget = ET.targetUser; + resultMsg = ET.resultMsg; + } + + if (args?.[1]) { + for (const ARG of args) { + if (ARG === "--" || ARG.trim().length < 1) continue; + if (ARG.startsWith("d ")) { + const U = ARG.slice(2).trim(); + if (U.length > 0 && !/\D/.test(U)) daysToDeleteMessage = parseInt(U, 10); else return trySend(msg.client, msg, "Invalid number of days to delete messages!"); + continue; + } + else if (CHECK_FOR_DURATION_REGEXP.test(ARG.trim())) + pDuration = duration(msg.editedAt || msg.createdAt, ARG.trim()); else reason = ARG.trim(); + } + } + + if (!pDuration.invoked) pDuration.invoked = DateTime.fromJSDate(msg.editedAt || msg.createdAt); + + if (execTarget.length > 0) { + for (const U of execTarget) { + + } + } + } +} \ No newline at end of file diff --git a/cmds/moderation/mute.js b/cmds/moderation/mute.js index 192fe0c..8964605 100644 --- a/cmds/moderation/mute.js +++ b/cmds/moderation/mute.js @@ -13,6 +13,7 @@ const durationFn = fn.duration; const targetUser = require("./src/targetUser"); const configureMuteRole = require("./src/configureMuteRole"); const { makeJSONMessage } = require("../../resources/debug"); +const createInfraction = require("./src/createInfraction"); Settings.defaultZone = "utc"; /*{ @@ -82,12 +83,13 @@ module.exports = class mute extends commando.Command { if (args?.[1]) { for (const ARG of args) { + if (ARG === "--" || ARG.trim().length < 1) continue; const U = ARG.slice(2).trim(); if (/^cmr(\s|$)/.test(ARG)) return configureMuteRole(msg, ARG.slice(3).trim()); if (/^s(\s|$)/.test(ARG)) return muteSetting(msg, U); - if (/^[\-\+]?\d{1,16}(?![^ymwdhs])[ymwdhs]?o?/i.test(ARG.trim())) { + if (fn.CHECK_FOR_DURATION_REGEXP.test(ARG.trim())) { duration = durationFn(msg.editedAt || msg.createdAt, ARG.trim()); - } else if (!ARG || ARG === "--" || ARG.trim().length === 0) continue; else reason = ARG.trim(); + } else reason = ARG.trim(); } } else if (!MUTE.role || !msg.guild.roles.cache.get(MUTE.role)) { return trySend(this.client, msg, `No mute role configured!\n\n**[ADMINISTRATOR]**\nRun \`${msg.guild.commandPrefix + this.name} --s -r role_[name|ID|mention] -d [duration]\` to set it up.\n` + @@ -110,19 +112,8 @@ module.exports = class mute extends commando.Command { resultMsg = FR.resultMsg; } else return trySend(this.client, msg, "Args: `<[user_[mention|ID|name]]> -- [reason] -- [duration]`. Use `,` to provide multiple user. `--s` to view settings.\nExample:```js\n" + `${msg.guild.commandPrefix + this.name} 580703409934696449, @Shasha#1234, ur mom,#6969,^yuck\\s(ur)?\\s.{5}#\\d+69$--69y69mo69w420d420h420m420s -- Saying "joe"\`\`\``); - let infractionToDoc; if (targetUsers.length > 0) { - let infractionCase = msg.guild.DB.moderation.infractions?.length, - muted = [], cant = [], already = [], infractionN = []; - - infractionToDoc = { - infraction: infractionCase ? infractionCase++ : 1, - by: targetUsers, - moderator: msg.author, - punishment: "mute", - reason: reason, - msg: msg.toJSON() - } + let muted = [], cant = [], already = [], infractionN = []; for (const EXEC of targetUsers) { try { @@ -142,12 +133,13 @@ module.exports = class mute extends commando.Command { EXEC.createDM().then(r => trySend(msg.client, r, emb)); } - if (muted.length > 0) { - infractionToDoc.executed = muted; - infractionToDoc.aborted = already; - infractionToDoc.failed = cant; - msg.guild.addInfraction(infractionToDoc); - } + let infractionToDoc = createInfraction(msg, targetUsers, "mute", reason); + + infractionToDoc.executed = muted; + infractionToDoc.aborted = already; + infractionToDoc.failed = cant; + + if (muted.length > 0) msg.guild.addInfraction(infractionToDoc); const NAME = msg.guild.id + "/" + infractionToDoc.infraction, newUnmuteSchedule = { diff --git a/cmds/moderation/src/createInfraction.js b/cmds/moderation/src/createInfraction.js new file mode 100644 index 0000000..b78c994 --- /dev/null +++ b/cmds/moderation/src/createInfraction.js @@ -0,0 +1,21 @@ +'use strict'; + +const { Message, User } = require("discord.js"); + +/** + * @param {Message} msg + * @param {User[]} targetUsers + * @param {["ban"|"mute"|"kick"|"strike"]} punishment + * @param {string} reason + */ +module.exports = (msg, targetUsers, punishment, reason) => { + let infractionCase = msg.guild.DB.moderation.infractions?.length; + return { + infraction: infractionCase ? infractionCase++ : 1, + by: targetUsers, + moderator: msg.author, + punishment: punishment, + reason: reason, + msg: msg.toJSON() + } +} \ No newline at end of file diff --git a/cmds/moderation/src/duration.js b/cmds/moderation/src/duration.js index d72d1ab..eefc891 100644 --- a/cmds/moderation/src/duration.js +++ b/cmds/moderation/src/duration.js @@ -2,7 +2,8 @@ const { DateTime, Settings, Interval } = require("luxon"), DURATION_REGEXP = /[\-]?\d+(?![^ymwdhs])[ymwdhs]?o?/gi, - DT_PRINT_FORMAT = "DDD'\n'cccc',' tt"; + DT_PRINT_FORMAT = "DDD'\n'cccc',' tt", + CHECK_FOR_DURATION_REGEXP = /^[\-\+]?\d{1,16}(?![^ymwdhs])[ymwdhs]?o?/i; Settings.defaultZone = "utc"; /** @@ -90,4 +91,4 @@ function duration(base, string) { return { invoked: DT_INVOKED, until: DT_END, interval: DT_INTERVAL, duration: intervalToDuration(DT_INTERVAL) } } -module.exports = { duration, DT_PRINT_FORMAT, intervalToDuration } \ No newline at end of file +module.exports = { duration, DT_PRINT_FORMAT, intervalToDuration, DURATION_REGEXP, CHECK_FOR_DURATION_REGEXP } \ No newline at end of file diff --git a/cmds/moderation/src/targetUser.js b/cmds/moderation/src/targetUser.js index d5a8cb0..99f2e31 100644 --- a/cmds/moderation/src/targetUser.js +++ b/cmds/moderation/src/targetUser.js @@ -10,7 +10,7 @@ const { cleanMentionID, findMemberRegEx } = require("../../../resources/function * @param {string} resultMsg * @returns {Promise<{ targetUser: User[], resultMsg: string }>} */ -module.exports = async (msg, mentions = [], targetUser = [], resultMsg) => { +module.exports = async (msg, mentions = [], targetUser = [], resultMsg = "") => { if (mentions.length === 0) throw new TypeError("Mentions has no length"); for (const usermention of mentions) { if (usermention.length > 0) { diff --git a/cmds/owner/unknown-command.js b/cmds/owner/unknown-command.js index 331e0be..0e7dd08 100644 --- a/cmds/owner/unknown-command.js +++ b/cmds/owner/unknown-command.js @@ -18,7 +18,7 @@ module.exports = class UnknownCommandCommand extends Command { // eslint-disable-next-line async run(msg) { if (msg.guild && !msg.member.hasPermission("MANAGE_MESSAGES")) return; - if (new RegExp("^<@\!?" + msg.client.user.id + ">\s").test(msg.content)) { + if (new RegExp("^<@\!?" + msg.client.user.id + ">.").test(msg.content)) { msg.channel.startTyping(); const s = msg.cleanContent.slice((msg.guild ? msg.guild.member(msg.client.user).displayName.length : msg.client.user.username.length) + 2).trim(); return trySend(msg.client, msg, await chatAnswer(s)); diff --git a/cmds/profile/avatar.js b/cmds/profile/avatar.js index 09f48f4..944d769 100644 --- a/cmds/profile/avatar.js +++ b/cmds/profile/avatar.js @@ -16,7 +16,7 @@ module.exports = class avatar extends commando.Command { }); } async run(msg, arg) { - const doc = msg.guild ?? msg.author; + const doc = msg.guild.DB.settings || msg.author.DB; const footerQuote = doc.defaultEmbed?.footerQuote; const option = "";// || arg.match(/(?"); if (TM.description) emb.setDescription(TM.description); if (MEM) { const RI = MEM.roles.cache.sort((a, b) => b.position - a.position).map(r => r.id).slice(0, -1), - RFS = splitOnLength(RI, 1010, ">, <@&"); - emb.addField("Joined", DateTime.fromJSDate(MEM.joinedAt).toFormat(DT_PRINT_FORMAT)) + RFS = splitOnLength(RI, 1010, ">, <@&"), INT = Interval.fromDateTimes(DateTime.fromJSDate(MEM.joinedAt), DateTime.now()); + emb.addField("Joined", "\n(${intervalToDuration(INT).strings.join(" ")} ago)`) .addField("Nick", `\`${MEM.displayName}\``); if (RFS[0]?.length > 0) { for (const p of RFS) { diff --git a/cmds/profile/servav.js b/cmds/profile/servav.js index a35f6cf..a3f7b66 100644 --- a/cmds/profile/servav.js +++ b/cmds/profile/servav.js @@ -9,8 +9,8 @@ const getColor = require("../../resources/getColor"); module.exports = class servav extends commando.Command { constructor(client) { super(client, { - name: "serv-av", - memberName: "serv-av", + name: "servav", + memberName: "servav", aliases: ["server-avatar", "serv-avatar", "server-av"], group: "profile", description: "Show server avatar." @@ -20,7 +20,7 @@ module.exports = class servav extends commando.Command { const server_ID = arg.split(/ +/)[0]; const doc = msg.guild?.id ?? msg.author.id; const col = database.collection(msg.guild ? "Guild" : "User"); - col.findOne({document: doc}, (err, res) => { + col.findOne({ document: doc }, (err, res) => { if (err) { errLog(err, msg, this.client); } @@ -36,15 +36,15 @@ module.exports = class servav extends commando.Command { target = msg.guild; } if (target) { - icon = target.iconURL({size:4096, dynamic:true}); + icon = target.iconURL({ size: 4096, dynamic: true }); } else { return trySend(this.client, msg, "I'm not in that server..."); } if (icon) { let embed = new MessageEmbed() - .setImage(icon) - .setTitle(target.name) - .setFooter(footerQuote ?? ""); + .setImage(icon) + .setTitle(target.name) + .setFooter(footerQuote ?? ""); if (target.owner.displayColor) { const color = getColor(target.owner.displayColor) embed.setColor(color); diff --git a/cmds/utility/setfootq.js b/cmds/utility/setfootq.js index 8766d65..cb1a88a 100644 --- a/cmds/utility/setfootq.js +++ b/cmds/utility/setfootq.js @@ -8,22 +8,17 @@ module.exports = class setfootq extends commando.Command { constructor(client) { super(client, { name: "setfootq", - aliases:["setfooterquote"], + aliases: ["setfooterquote"], memberName: "setfootq", group: "utility", description: "Set server embed footer text.", - userPermissions: ["ADMINISTRATOR"] + userPermissions: ["MANAGE_GUILD"] }); } async run(msg, args) { try { - if (msg.guild ? !msg.guild.member(msg.author).hasPermission("MANAGE_GUILD") : false && !this.client.owners.includes(msg.author)) { - return trySend(this.client, msg, 'No lol'); - } - let oldQ = msg.guild?.defaultEmbed ?? msg.author.defaultEmbed; - if (!oldQ) { - oldQ = {}; - } + let oldQ = msg.guild ? msg.guild.DB.settings.defaultEmbed : msg.author.DB.defaultEmbed; + if (!oldQ) oldQ = {}; const newQ = oldQ?.footerQuote; oldQ.footerQuote = args.trim(); const r = msg.guild ? msg.guild.setDefaultEmbed(oldQ) : msg.author.setDefaultEmbed(oldQ); diff --git a/resources/eventsLogger/guildMemberAdd.js b/resources/eventsLogger/guildMemberAdd.js index a9ffa5d..c93511d 100644 --- a/resources/eventsLogger/guildMemberAdd.js +++ b/resources/eventsLogger/guildMemberAdd.js @@ -20,7 +20,7 @@ module.exports = (member) => { .setTitle("`" + member.user.tag + "` joined") .setThumbnail(member.user.displayAvatarURL({ format: "png", size: 4096, dynamic: true })) .setColor(getColor("cyan")) - .addField("Registered", DateTime.fromJSDate(member.user.createdAt).toFormat(DT_PRINT_FORMAT), true) + .addField("Registered", "", true) .setDescription(`<@!${member.id}> (${member.id}) just joined.\nWe have ${member.guild.memberCount} total members now.`); return trySend(member.client, log, emb); } diff --git a/resources/eventsLogger/guildMemberRemove.js b/resources/eventsLogger/guildMemberRemove.js index a11abf9..98132ba 100644 --- a/resources/eventsLogger/guildMemberRemove.js +++ b/resources/eventsLogger/guildMemberRemove.js @@ -18,15 +18,14 @@ module.exports = (member) => { const emb = defaultEventLogEmbed(member.guild), RO = member.roles.cache.sort((a, b) => b.position - a.position).map(r => r.id).slice(0, -1), RU = splitOnLength(RO, 1010, ">, <@&"), - JO = DateTime.fromJSDate(member.user.createdAt), LE = DateTime.fromJSDate(member.joinedAt), INT = Interval.fromDateTimes(LE, DateTime.now()); emb .setTitle("`" + member.user.tag + "` left") .setThumbnail(member.user.displayAvatarURL({ format: "png", size: 4096, dynamic: true })) .setColor(getColor("yellow")) - .addField("Registered", JO.toFormat(DT_PRINT_FORMAT), true) - .addField("Joined", LE.toFormat(DT_PRINT_FORMAT) + `\n(${intervalToDuration(INT).strings.join(" ")} ago)`, true) + .addField("Registered", "", true) + .addField("Joined", "\n(${intervalToDuration(INT).strings.join(" ")} ago)`, true) .addField("Nick", "`" + member.displayName + "`") .setDescription(`<@!${member.id}> (${member.id}) just left.\nWe have ${member.guild.memberCount} total members now.`); for (const U of RU) { diff --git a/resources/structures.js b/resources/structures.js index a48a419..094b592 100644 --- a/resources/structures.js +++ b/resources/structures.js @@ -1,6 +1,6 @@ 'use strict'; -const { Structures, Guild, GuildMember } = require("discord.js"), +const { Structures, Guild, GuildMember, BanOptions } = require("discord.js"), { database } = require("../database/mongo"), { errLog } = require("./functions"); const { DateTime, Duration } = require("luxon"); @@ -20,7 +20,8 @@ Structures.extend("Guild", u => { if (!r.moderation) r.moderation = {}; if (!r.settings.eventChannels) r.settings.eventChannels = {}; if (!r.moderation.settings) r.moderation.settings = {}; - if (!r.moderation.infractions) r.moderation.infractions = []; + if (!r.moderation.infractions) r.moderation.infractions = new Map(); + if (!r.moderation.timedPunishments) r.moderation.timedPunishments = new Map(); return this.DB = r; }); } @@ -48,23 +49,12 @@ Structures.extend("Guild", u => { /** * Get user infractions * @param {String} get - User ID - * @returns {Promise} Array of infractions objects + * @returns {Promise} Array of infractions objects */ async getInfractions(get) { try { if (!this.DB) await this.dbLoad(); - let found = []; - if (this.DB.moderation.infractions.length > 0) { - for (const inf of this.DB.moderation.infractions) { - for (const user of inf.by) { - if (user.id === get) { - found.push(inf); - break; - } - } - } - } - return found; + return this.DB.moderation.infractions.filter(r => r.map(u => u.id).includes(get)); } catch (e) { } } @@ -265,6 +255,14 @@ Structures.extend("User", u => { return ret; } } + + /** + * @param {Guild} guild + * @param {BanOptions} option + */ + async ban(guild, option) { + guild.members.ban(this, option); + } } });