From 88ee8450a52de66eff1e25b31bc2bffec686cf93 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Thu, 19 Nov 2020 20:19:18 -0700 Subject: [PATCH] Add warnings (unfinished) and almost finish santa --- commands/secretsanta.js | 152 ++++++++++++++++++++++++++++++++++++---- commands/warn.js | 126 +++++++++++++++++++++++++++++++++ models/mod.js | 13 ++++ models/secretsanta.js | 17 +++++ test.js | 50 ++++++++++--- util/ask.js | 6 +- 6 files changed, 340 insertions(+), 24 deletions(-) create mode 100644 commands/warn.js create mode 100644 models/mod.js create mode 100644 models/secretsanta.js diff --git a/commands/secretsanta.js b/commands/secretsanta.js index 407efd6..48ec162 100644 --- a/commands/secretsanta.js +++ b/commands/secretsanta.js @@ -1,4 +1,5 @@ const Discord = require('discord.js'); +const SS = require('../models/secretsanta'); const ask = require('../util/ask'); @@ -11,31 +12,158 @@ module.exports = { .addField("Syntax", "``"), async execute(message, msg, args, cmd, prefix, mention, client) { if (!args.length) {return message.channel.send(`Syntax: \`${prefix}\``);} - if (['start', 'create', 'new'].includes(args[0].toLowerCase())) { + if (['start', 'create', 'new', 'c', 'n', 's'].includes(args[0].toLowerCase())) { + function clearDM() {client.misc.activeDMs.delete(message.author.id);} if (client.misc.activeDMs.has(message.author.id)) {return message.reply("I'm already asking you questions in DM for a separate command! Finish that command before using this one.");} client.misc.activeDMs.set(message.author.id, 'secretsanta-make'); let mesg = await message.author.send("I'll be asking you a few questions here about how you want your Secret Santa to go! You can simply ignore the messages for a few minutes to cancel the process.").catch(() => {message.reply("Please open your DMs so I can ask you some questions!");}); let dmch = mesg.channel; - let conf = await ask(mesg, "This secret santa will be tied to your account, and you will be considered the host. Is this okay?", 60000); if (!conf) {return;} - if (['n', 'no'].includes(conf.trim().toLowerCase())) {return dmch.send("Oh, alrighty! Have the person who wants to be the host execute this same command.");} - if (!['yes', 'ye', 'y', 'sure'].includes(conf.trim().toLowerCase())) {return dmch.send("Please specify yes or no you weeb!");} + let conf = await ask(mesg, "This secret santa will be tied to your account, and you will be considered the host. Is this okay?", 60000, true); if (!conf) {return clearDM();} + if (['n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Oh, alrighty! Have the person who wants to be the host execute this same command.");} + if (!['yes', 'ye', 'y', 'sure'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Please specify yes or no you weeb!");} - let start = await ask(mesg, "When will you begin the secret santa? (You'll start it manually, so don't worry about formatting.", 60000); if (!start) {return;} - if (start.length > 150) {return dmch.send("Heya there, just a few words, please! I don't wanna have to read out an essay about when it's starting to all the people that want to hear about your secret santa!");} + let start = await ask(mesg, "When will you begin the secret santa? (You'll start it manually, so don't worry about formatting.", 60000, true); if (!start) {return clearDM();} + if (start.length > 150) {clearDM(); return dmch.send("Heya there, just a few words, please! I don't wanna have to read out an essay about when it's starting to all the people that want to hear about your secret santa!");} - let end = await ask(mesg, "When will you end the secret santa? (You'll also end it manually.)", 60000); if (!start) {return;} - if (end.length > 150) {return dmch.send("Heya there, just a few words, please! I don't wanna have to read out an essay about when it's ending to all the people that want to hear about your secret santa!");} + let end = await ask(mesg, "When will you end the secret santa? (You'll also end it manually.)", 60000, true); if (!end) {return clearDM();} + if (end.length > 150) {clearDM(); return dmch.send("Heya there, just a few words, please! I don't wanna have to read out an essay about when it's ending to all the people that want to hear about your secret santa!");} - let spend = await ask(mesg, "What is your maximum and minimum spending? This is useful so that everyone gets an equal gift or gifts. This will be shown to the people that buy their gifts.", 360000); if (!spend) {return;} - if (spend.length > 500) {return dmch.send("Mate, this is not a dissertation! Let's keep it under 500 characters, please!");} + let spend = await ask(mesg, "What is your maximum and minimum spending? This is useful so that everyone gets an equal gift or gifts. This will be shown to the people that buy their gifts.", 360000, true); if (!spend) {return clearDM();} + if (spend.length > 500) {clearDM(); return dmch.send("Mate, this is not a dissertation! Let's keep it under 500 characters, please!");} - let anon = await ask(mesg, "Will you be keeping this secret santa totally anonymous, or will you let the gift recipients know who their gifters are when they are opened?", 360000); if (!anon) {return;} + let anon = await ask(mesg, "Will you be keeping this secret santa totally anonymous, or will you let the gift recipients know who their gifters are when they are opened? Type \"yes\" if you will be keeping it anonymous, and \"no\" otherwise.", 360000, true); if (!anon) {return clearDM();} if (['n', 'no'].includes(anon.trim().toLowerCase())) {anon = false;} else if (['yes', 'ye', 'y', 'sure'].includes(anon.trim().toLowerCase())) {anon = true;} - else {return dmch.send("Please specify yes or no you weeb!");} + else {clearDM(); return dmch.send("Please specify yes or no you weeb!");} + let info = await ask(mesg, "What information would you like me to ask joining members to provide when they join your secret santa? (Whatever you type will be shown directly to them when.)", 360000, true); if (!info) {return clearDM();} + if (info.length > 750) {clearDM(); return dmch.send("Let's keep it under 750 characters, please.");} + let notes = await ask(mesg, "Are there any other notes you'd like to add? If not, just write N/A or something of that nature.", 360000, true); if (!ask) {return clearDM();} + if (notes.length > 500) {clearDM(); return dmch.send("Let's keep it under 500 characters, please.");} + + let fconf = await ask(mesg, "Would you like to continue and create the secret santa? By doing so, you agree that:\n-Natsuki and its developers are not responsible for anything financially-related to your secret santa\n-Anyone with your join code can join the secret santa\n-You are responsible for notifying your members of any changes or updates.\n-I am not responsible for any eggnog that may or may not be stolen from you by Wubzy. *for legal reasons this is a joke*\n\n-The answers you have submitted are what you want to use, as they cannot be changed.", 120000, true); if (!fconf) {return clearDM();} + if (['n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Oh, yikes. Is it about the eggnog? Sorry about that hehe...");} + if (!['yes', 'ye', 'y', 'sure'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Please specify yes or no you weeb!");} + + let id; + while (true) { + id = require('../util/makeid')(6); + let test = await SS.findOne({ssid: id}); + if (!test) {break;} + } + + let tss = new SS({ + ssid: id, + owner: message.author.id, + start: start, + end: end, + anon: anon, + spend: spend, + info: info, + notes: notes, + members: [], + started: false + }); + tss.save(); + + clearDM(); + return dmch.send(new Discord.MessageEmbed() + .setTitle("Secret Santa Created!") + .setDescription("Your Secret Santa has been completed! Have your members join by using `n?secretsanta join ` where the ID is the ID displayed below. You can start your secret santa when you have at least 3 members with `n?secretsanta start `. If someone joins that you don't want in your secret santa, use `n?secretsanta kick <@member|userID>`. If you want to also participate, just join the same way as everyone else.") + .setThumbnail(message.author.avatarURL({size: 1024})) + .addField("ID", `\`${id}\``, true) + .addField("Owner", message.author.username, true) + .setColor("01bd2f") + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp() + ); + } + + if (['j', 'join'].includes(args[0].toLowerCase())) { + if (!args[1] || args[1].length !== 6) {return message.channel.send("You must provide a 6-digit join code! Ask the host to copy it and send it to you.");} + + let tss = await SS.findOne({ssid: args[1]}); + if (!tss) {return message.channel.send("A secret santa with that join code does not exist!");} + if (tss.started) {return message.channel.send("That secret santa has already started.");} + let min = false; let m; for (m of tss.members) {if (m.id === message.author.id) {min = true;}} + if (tss.members && min) {return message.channel.send("You're already in that secret santa!");} + + function clearDM() {client.misc.activeDMs.delete(message.author.id);} + if (client.misc.activeDMs.has(message.author.id)) {return message.reply("I'm already asking you questions in DM for a separate command! Finish that command before using this one.");} + client.misc.activeDMs.set(message.author.id, 'secretsanta-join'); + + let mesg = await message.author.send("I'll be asking you a few questions here about you and what you want! You can simply ignore the messages for a few minutes to cancel the process.").catch(() => {message.reply("Please open your DMs so I can ask you some questions!");}); + let dmch = mesg.channel; + + let o = await client.users.fetch(tss.owner); + + await dmch.send(new Discord.MessageEmbed() + .setTitle("This Secret Santa!") + .setDescription("This is the one you're trying to join!") + .addField("Start", tss.start) + .addField("End", tss.end) + .addField("Spending", tss.spend) + .addField("Notes", tss.notes) + .addField("Anonymous Gifters", tss.anon ? "Yes" : "No") + .addField("ID", `\`${tss.ssid}\``, true) + .addField("Owner", o.username, true) + .addField("Members", tss.members ? tss.members.length : 0, true) + .setColor("01bd2f") + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp() + ); + + let name = await ask(mesg, "What is your name? This can be seen by everyone in the secret santa.", 60000, true); if (!name) {return clearDM();} + if (name.length > 50) {clearDM(); return dmch.send("Maybe just the *first* name? I doubt it's over 50 characters.");} + + await dmch.send("This is the information the host has asked you to provide:"); + let info = await ask(mesg, tss.info, 600000, true); if (!info) {return clearDM();} + if (info.length > 750) {clearDM(); return dmch.send("Let's keep that under 750 characters. No need to put your entire Christmas list on there :smirk:");} + + let conf = await ask(mesg, "Before we finish, do you agree to the following things:\n-I, Natsuki, and my developers, are not responsible for anything financially-related to your Secret Santa\n-You should contact the host if you have questions\n-These answers you gave are final and will be seen by the person who draws you.\n-You *need* to have your DMs open so that I can reach you when drawing time comes!", 120000, true); + if (['n', 'no'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Alrighty! I've discarded your responses :P");} + if (!['yes', 'ye', 'y', 'sure'].includes(conf.trim().toLowerCase())) {clearDM(); return dmch.send("Please specify yes or no you weeb!");} + + let tssmembers = tss.members ? tss.members : {}; + tssmembers.push({id: message.author.id, name: name, info: info}); + tss.members = tssmembers; + tss.save(); + + clearDM(); + + await o.send(`${message.author.username} has joined your Secret Santa! (Code: \`${tss.ssid}\` in case you have more than one)`); + return dmch.send("All done! You've now joined."); + } + + if (['start'].includes(args[0])) { + if (!args[1] || args[1].length !== 6) {return message.channel.send("You must specify the join code/ID to your Secret Santa!");} + let tss = await SS.findOne({ssid: args[1]}); + if (!tss) {return message.channel.send("That Secret Santa doesn't exist; your code is invalid!");} + if (tss.owner !== message.author.id) {return message.channel.send("You must be the host to do that!");} + if (tss.started) {return message.channel.send("Your Secret Santa is already started!");} + if (tss.members.length < 3) {return message.channel.send("You need to have at least 3 members in order to start.");} + + let dm = []; let cm; let rm; let rm2; + while (true) { + rm = tss.members[Math.floor(Math.random() * tss.members.length)]; + rm2 = tss.members[Math.floor(Math.random() * tss.members.length)]; + if (rm.id !== rm2.id) { + dm.push([rm, rm2]); + break; + } + } + let i; for (i=0;i Warnings") + .setDescription("") + .addField("Syntax", "`warn <@member> `") + .addField("Notice", "You must be a server administrator in order to use this command."), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!message.guild) {return message.channel.send("This is a server-only command.");} + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}warn <@member> \``);} + if (!message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You must be a server administrator to use this command.");} + if (args.length < 2 && !['check', 'c', 'list', 'l', 'clear', 'e', 'empty'].includes(args[0].toLowerCase())) {return message.channel.send("You must provide a reason for warning the user, or `check` or `clear`.");} + + let user = message.mentions.members.first() && args[0].match(/^<@(?:!)(?:\d+)>$/) ? message.mentions.members.first() : message.guild.members.cache.has(args[0]) ? message.guild.members.cache.get(args[0]) : null; + if (!user && args.length > 1) {return message.channel.send("Either you didn't mention a user, or I can't find that user!");} + if (args.length > 1) {args.shift();} + + if (['check', 'c', 'list', 'l'].includes(args[0].toLowerCase())) { + user = user ? user : message.member; + let mh = await Mod.findOne({gid: message.guild.id}); + if (!mh || !mh.warnings.size) {return message.reply("There are no warnings available in this server.");} + + if (!mh.warnings.has(user.id) || !mh.warnings.get(user.id).length) {return message.reply(`${user.id === message.author.id ? 'You have' : 'That user has'} never been warned in this server.`);} + console.log(mh.cases, mh.warnings); + let ws = ''; + let cwc = 0; + let warning; for (warning of mh.warnings.get(user.id)) { + let tcase = mh.cases.get(`${warning}`); + console.log(tcase.status, warning); + if (tcase.status !== "Cleared") { + ws += `\`Case #${warning}\` - Issued by <@${tcase.moderators[0]}>\n${tcase.reason}\n\n`; + } else {cwc++;} + } + if (cwc > 0) {ws += '*Plus ' + cwc + ' other warnings that have been cleared.*';} + if (cwc === mh.warnings.get(user.id).length) {return message.reply("That user has no uncleared warnings.");} + return message.channel.send(new Discord.MessageEmbed() + .setTitle("User Warnings") + .setThumbnail(client.users.cache.get(user.id).avatarURL({size: 1024})) + .setDescription(`For ${user.displayName}`) + .addField("Warnings", ws) + .setColor("c375f0") + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp() + ); + } + + else if (['clear', 'e', 'empty'].includes(args[0].toLowerCase())) { + user = user ? user : message.member; + let mh = await Mod.findOne({gid: message.guild.id}); + if (!mh || !mh.warnings.size) {return message.reply("There are no warnings available in this server.");} + + if (!mh.warnings.has(user.id) || !mh.warnings.get(user.id).length) {return message.reply(`${user.id === message.author.id ? 'You have' : 'That user has'} never been warned in this server.`);} + + let mhcases = mh.cases; + let cwc = 0; var wc = 0; + let warning; for (warning of mh.warnings.get(user.id)) { + if (mhcases.get(`${warning}`).status !== "Cleared") { + let tcase = mhcases.get(`${warning}`); + tcase.status = "Cleared"; + tcase.history.push(`${new Date().toISOString()} - ${message.author.username} - Cleared the warning.`); + wc++; + if (!tcase.moderators.includes(message.author.id)) {tcase.moderators.push(message.author.id);} + mhcases.set(`${warning}`, tcase); + } else {cwc++;} + } + if (cwc === mh.warnings.get(user.id).length) {return message.reply("That user has no uncleared warnings.");} + + mh.cases = mhcases; + mh.save(); + return message.reply(`Cleared ${wc} warnings from ${user.displayName}.`); + } + + else { + if (user.id === message.author.id) {return message.channel.send("You can't warn yourself!");} + if (user.id === client.user.id) {return message.channel.send("You can't warn me, silly! What do you want me to do, spank myself?");} + if (client.users.cache.get(user.id).bot) {return message.channel.send("You can't warn a bot!");} + + let options = new TagFilter([ + new Tag(['r', 'reason'], 'reason' ,'append'), + new Tag(['nd', 'nm', 'nomessage', 'nodm'], 'nodm' ,'toggle'), + new Tag(['silent', 's'], 'silent', 'toggle'), + new Tag(['note', 'n', 'notes'], 'notes', 'append') + ]).test(args.join(" ")); + + if (Object.keys(options).length && (!options.reason || !options.reason.length)) {return message.channel.send("You *must* use -r or -reason to specify your reason if you include any other tags. Please try again!");} + let reason = options.reason && options.reason.length ? options.reason : args.join(" "); + if (reason.length > 200) {return message.reply("Please keep your reason short and sweet - less than 200 characters, please!");} + if (options.notes && options.notes.length > 150) {return message.reply("Please keep your notes short and sweet - less than 150 characters, please!");} + let notes = options.notes && options.notes.length ? [options.notes] : []; + + let mh = await Mod.findOne({gid: message.guild.id}) || new Mod({gid: message.guild.id}); + + let mhcases = mh.cases; + mhcases.set(`${mh.cases.size + 1}`, { + members: [user.id], + punishment: "Warned", + reason: reason, + status: "Closed", + moderators: [message.author.id], + notes: notes, + history: [`${new Date().toISOString()} - ${message.author.username} - Created case`, `${new Date().toISOString()} - ${message.author.username} - Warned ${client.users.cache.get(user.id).username}`], + issued: new Date().toUTCString() + }); + + let mhwarnings = mh.warnings; + console.log(mhwarnings); + console.log(mhcases.size); + if (mhwarnings.has(user.id)) {var uw = mhwarnings.get(user.id); uw[mhcases.size - 1] = mhcases.size;} + mhwarnings.set(user.id, mhwarnings.has(user.id) ? uw : [mhcases.size]); + mh.warnings = mhwarnings; + mh.cases = mhcases; + + if (!options.silent) {message.channel.send(`Case ${mh.cases.size} - Member has been warned. Reason: \`${reason}\``);} + if (!options.silent && !options.nodm) {client.users.cache.get(user.id).send(`\`${message.author.username}\` has warned you in \`${message.guild.name}\`. Reason: **${reason}**`);} + + mh.save(); + return null; + } + } +}; \ No newline at end of file diff --git a/models/mod.js b/models/mod.js new file mode 100644 index 0000000..b248801 --- /dev/null +++ b/models/mod.js @@ -0,0 +1,13 @@ +const mongoose = require('mongoose'); + +const ModModel = new mongoose.Schema({ + gid: {type: String, unique: true}, + cases: {type: Map, default: new Map()}, + rules: {type: Map, default: new Map()}, + auto: {type: Map, default: new Map()}, + warnings: {type: Map, default: new Map()}, + maxWarnings: {type: Number, default: 0}, + onMaxWarn: {type: String, default: 'kick'} +}); + +module.exports = mongoose.model('mod', ModModel); \ No newline at end of file diff --git a/models/secretsanta.js b/models/secretsanta.js new file mode 100644 index 0000000..7f591cb --- /dev/null +++ b/models/secretsanta.js @@ -0,0 +1,17 @@ +const mongoose = require('mongoose'); + +const SS = new mongoose.Schema({ + ssid: {type: String, unique: true}, + owner: String, + start: String, + end: String, + anon: Boolean, + info: String, + notes: String, + members: [{name: String, id: String, info: String}], + started: Boolean, + spend: String, + assignments: [{name: String, assignedTo: String}] +}); + +module.exports = mongoose.model('ss', SS); \ No newline at end of file diff --git a/test.js b/test.js index f35786b..94412ed 100644 --- a/test.js +++ b/test.js @@ -1,10 +1,42 @@ -const {TagFilter} = require("./util/tagfilter"); -const {Tag} = require("./util/tag"); +let tss = { + members: [ + { + name: "wubzy", + id: "4545", + info: "stuff" + }, + { + name: "slushie", + id: "3434", + info: "wow" + }, + { + name: "kyusa", + id: "6767", + info: "e" + }, + { + name: "swag", + id: "8989", + info: "xd" + }, + { + name: "doge", + id: "0101", + info: "homks" + }, + { + name: "vincent", + id: "6666", + info: "shrekt" + } + ], + assignments: [] +} -console.log(new TagFilter([ - new Tag(['n', 'name'], 'name', 'append'), - new Tag(['desc', 'd'], 'description', 'append'), - new Tag(['f', 'force'], 'force', 'toggle'), - new Tag(['option', 'o'], 'options', 'listAppend'), - new Tag(['test', 't'], 'test', 'listAppend') -]).test('blah blah blah -n bonk -d stonks very stonks -f -t some stuff -o an option -test blah blah blah -o another optionl -o hecc -o hecc 2 -test such wow, very flex -t homks.exe has stopped working')); \ No newline at end of file +let dm = []; let cm; let rm; let rm2; + +let mg; let asg = []; for (mg of dm) {asg.push({name: mg[0].id, assignedTo: mg[1].id});} +tss.assignments = asg; + +console.log(tss); \ No newline at end of file diff --git a/util/ask.js b/util/ask.js index 4151a7c..7418954 100644 --- a/util/ask.js +++ b/util/ask.js @@ -1,8 +1,8 @@ -module.exports = async (message, toAsk, time) => { +module.exports = async (message, toAsk, time, nf) => { let msg = await message.channel.send(toAsk); - let filter = m => m.author.id === message.author.id; + let filter = nf ? () => true : m => m.author.id === message.author.id; try { - let collected = await message.channel.awaitMessages(filter, {max: 1, errors: ['time'], time: time}); + let collected = await msg.channel.awaitMessages(filter, {max: 1, errors: ['time'], time: time}); collected = collected.first().content; return collected; } catch {