From 0c3070ef0ff0be7204b5b645566ac9418c0a2d30 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 17 Oct 2020 01:31:57 -0600 Subject: [PATCH 01/41] start pagination class --- models/anime.js | 20 ++++++++ models/user.js | 2 + util/ts/pagination.ts | 112 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 models/anime.js create mode 100644 util/ts/pagination.ts diff --git a/models/anime.js b/models/anime.js new file mode 100644 index 0000000..6af1ed6 --- /dev/null +++ b/models/anime.js @@ -0,0 +1,20 @@ +const mongoose = require('mongoose'); + +const AniSchema = new mongoose.Schema({ + id: {type: String, unique: true}, + name: String, + plot: String, + publishers: [String], + studio: [String], + airStartDate: Date, + airEndDate: Date, + isComplete: Boolean, + seasons: Number, + episodes: Number, + genres: [String], + tags: [String], + characters: [String], + streamAt: [String] +}); + +module.exports = mongoose.model('anime', AniSchema); \ No newline at end of file diff --git a/models/user.js b/models/user.js index fb9122c..29e5e29 100644 --- a/models/user.js +++ b/models/user.js @@ -8,6 +8,8 @@ const UserSchema = new mongoose.Schema({ statusclearmode: {type: String, default: 'auto'}, statusclearat: {type: Date, default: null}, statussetat: {type: Date, default: null}, + statusshowcleartime: {type: Boolean, default: true}, + statusshowsettime: {type: Boolean, default: true}, support: {type: Boolean, default: false}, staff: {type: Boolean, default: false}, admin: {type: Boolean, default: false}, diff --git a/util/ts/pagination.ts b/util/ts/pagination.ts new file mode 100644 index 0000000..3f3ce96 --- /dev/null +++ b/util/ts/pagination.ts @@ -0,0 +1,112 @@ +import {MessageEmbed, Message, Client} from 'discord.js'; + +import wait = require('../../util/wait'); + +export class Pagination { + title: string; + pages: Page[]; + zeroPage: Page | MessageEmbed; + pageTemplate: MessageEmbed; + message: Message; + timeout: Number; + description: string; + activationMessage: Message; + client: Client; + currentpos: number = 0; + + + + constructor (title: string, pages: Page[], zeroPage: Page | MessageEmbed, client: Client, message: Message, activationMessage: Message, timeout: number, description?: string, pageTemplate?: MessageEmbed) { + this.title = title; + this.pages = pages; + this.zeroPage = zeroPage; + this.message = message; + this.timeout = timeout; + this.activationMessage = activationMessage; + this.client = client; + + this.description = description ? description : `Requested by ${activationMessage.guild ? activationMessage.member.displayName : activationMessage.author.username}.`; + + this.pageTemplate = pageTemplate + ? pageTemplate + : new MessageEmbed() + .setDescription(this.description) + .addField('Navigation', `Click or tap the arrows below this message to navigate through the pages!\n\n*This menu will timeout in ${this.timeout}ms.`) + .setColor('c375f0') + .setFooter('Natsuki', this.client.user.avatarURL()) + .setTimestamp(); + }; + + + + public addPage(page: Page): Pagination { + this.pages.push(page); + return this; + }; + + public render(pos: number): Pagination { + let page = this.pages[this.currentpos]; + let pageEmbed: MessageEmbed = new MessageEmbed() + .setTitle(`${this.title} -> ${page.title}`) + .setDescription(`${this.pageTemplate.description ? this.pageTemplate.description : this.description}\n\n${page.description}`) + .setColor(this.pageTemplate.hexColor ? this.pageTemplate.hexColor : 'c375f0') + .setFooter(this.pageTemplate.footer ? `${this.pageTemplate.footer.text} | Page ${this.currentpos + 1} of ${this.pages.length}` : `Natsuki | Page ${this.currentpos + 1} of ${this.pages.length}`) + .setTimestamp(); + let item: PageItem; for (item of page.items) {pageEmbed.addField(item.title, item.text);} + if (this.pageTemplate.thumbnail) {pageEmbed.setThumbnail(this.pageTemplate.thumbnail.url);} + + this.message.edit(pageEmbed); + + return this; + }; + + public nextPage(): Pagination { + return this.render(this.currentpos < (this.pages.length - 1) ? this.currentpos + 1 : this.currentpos); + }; + + public prevPage(): Pagination { + return this.render(this.currentpos > 0 ? this.currentpos - 1 : this.currentpos); + }; + + public destroy(delmsg?: boolean, fmsg?: Message): Pagination { + return this; + }; + + public resetTimeout(newTimeout?: number): Pagination { + return this; + }; + + public init(): Pagination { + return this; + }; + +} + + + +export class Page { + items: PageItem[] = []; + title: string; + description: string; + + + + constructor(title: string, items: PageItem[], description?: string) { + this.title = title; + this.items = items; + this.description = description; + }; + + + + public addItem(item: PageItem): Page { + this.items.push(item); + return this; + }; + +} + +interface PageItem { + title: string, + text: string +} \ No newline at end of file From 75ea1b60a381d29ed73524374aa3331ecfd11e5e Mon Sep 17 00:00:00 2001 From: Slushie Date: Wed, 21 Oct 2020 20:53:16 +0100 Subject: [PATCH 02/41] Create eee --- eee | 1 + 1 file changed, 1 insertion(+) create mode 100644 eee diff --git a/eee b/eee new file mode 100644 index 0000000..038d718 --- /dev/null +++ b/eee @@ -0,0 +1 @@ +testing From 97396f243fb0a8facb2d2cef72b68b8ff69779c4 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 23 Oct 2020 22:53:36 -0600 Subject: [PATCH 03/41] Begin pagination, add listAppend mode to TagFilter --- .gitignore | 3 ++- commands/userinfo.js | 1 + test.js | 10 ++++++++++ util/tag.d.ts | 2 +- util/tagfilter.d.ts | 2 +- util/tagfilter.js | 32 +++++++++++++++++++++++++++++--- util/ts/tag.ts | 2 +- util/ts/tagfilter.ts | 25 +++++++++++++++++++++---- 8 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 test.js diff --git a/.gitignore b/.gitignore index df9906b..8ac6dfb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ config.json -auth.json \ No newline at end of file +auth.json +test.js \ No newline at end of file diff --git a/commands/userinfo.js b/commands/userinfo.js index 2dcf690..9ce5d9b 100644 --- a/commands/userinfo.js +++ b/commands/userinfo.js @@ -11,6 +11,7 @@ module.exports = { let person = message.guild ? mention ? message.guild.members.cache.get(mention.id) : args[0] ? message.guild.members.cache.has(args[0]) ? message.guild.members.cache.get(args[0]) : message.member : message.member : message.author; let name = message.guild ? person.displayName : person.username; let tu = await UserData.findOne({uid: person.id}); + if (!tu) {return message.channel.send("I don't have any data on that user yet.");} let infoembed= new Discord.MessageEmbed() .setTitle(`User Info for ${name}`) .setDescription(`Requested by ${message.guild ? message.member.displayName : message.author.username}`) diff --git a/test.js b/test.js new file mode 100644 index 0000000..f35786b --- /dev/null +++ b/test.js @@ -0,0 +1,10 @@ +const {TagFilter} = require("./util/tagfilter"); +const {Tag} = require("./util/tag"); + +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 diff --git a/util/tag.d.ts b/util/tag.d.ts index 4421210..3c579ea 100644 --- a/util/tag.d.ts +++ b/util/tag.d.ts @@ -5,5 +5,5 @@ export declare class Tag { constructor(triggers: string[], tagName: string, filterType: TagFilterType); addTrigger(trigger: string): Tag; } -declare type TagFilterType = "append" | "toggle"; +declare type TagFilterType = "append" | "toggle" | "listAppend"; export {}; diff --git a/util/tagfilter.d.ts b/util/tagfilter.d.ts index 0da5548..ed480c4 100644 --- a/util/tagfilter.d.ts +++ b/util/tagfilter.d.ts @@ -6,5 +6,5 @@ export declare class TagFilter { constructor(tags: Tag[]); test(text: string): object; } -declare type TagFilterType = "append" | "toggle"; +declare type TagFilterType = "append" | "toggle" | "listAppend"; export {}; diff --git a/util/tagfilter.js b/util/tagfilter.js index ed77f12..d55ec5f 100644 --- a/util/tagfilter.js +++ b/util/tagfilter.js @@ -20,20 +20,40 @@ class TagFilter { test(text) { var filtered = {}; var reading = null; + var filterType; + var ticks = {}; let words = text.trim().split(/\s+/g); let word; for (word of words) { if (word.startsWith('-') && word.length > 1 && this.triggers.has(word.trim())) { - reading = this.filterTypes.get(this.triggers.get(word.trim())) == "toggle" ? null : word.trim(); + filterType = this.filterTypes.get(this.triggers.get(word.trim())); + reading = !['append', 'listAppend'].includes(filterType) ? null : word.trim(); if (!reading) { filtered[`${this.triggers.get(word.trim())}`] = true; } else { - filtered[`${this.triggers.get(reading)}`] = ''; + filtered[`${this.triggers.get(reading)}`] = filterType == 'append' ? '' : Array.isArray(filtered[`${this.triggers.get(reading)}`]) ? filtered[`${this.triggers.get(reading)}`] : []; + } + if (filterType == "listAppend") { + if (ticks[`${this.triggers.get(word.trim())}`] && ticks[`${this.triggers.get(word.trim())}`].length) { + filtered[`${this.triggers.get(word.trim())}`].push(ticks[`${this.triggers.get(word.trim())}`]); + } + ticks[`${this.triggers.get(word.trim())}`] = ''; } } else if (reading) { - filtered[`${this.triggers.get(reading)}`] = `${filtered[`${this.triggers.get(reading)}`]} ${word}`; + if (filterType == "listAppend") { + ticks[`${this.triggers.get(reading)}`] += ` ${word}`; + } + else { + filtered[`${this.triggers.get(reading)}`] = `${filtered[`${this.triggers.get(reading)}`]} ${word}`; + } + } + } + let tick; + for (tick of Object.keys(ticks)) { + if (ticks[tick].length) { + filtered[tick].push(ticks[tick]); } } let key; @@ -41,6 +61,12 @@ class TagFilter { if (typeof filtered[key] == 'string') { filtered[key] = filtered[key].trim(); } + else if (Array.isArray(filtered[key])) { + let subkey; + for (subkey of filtered[key]) { + filtered[key][filtered[key].indexOf(subkey)] = subkey.trim(); + } + } } return filtered; } diff --git a/util/ts/tag.ts b/util/ts/tag.ts index 86c7ce2..380d46f 100644 --- a/util/ts/tag.ts +++ b/util/ts/tag.ts @@ -22,4 +22,4 @@ export class Tag { }; } -type TagFilterType = "append" | "toggle"; \ No newline at end of file +type TagFilterType = "append" | "toggle" | "listAppend"; \ No newline at end of file diff --git a/util/ts/tagfilter.ts b/util/ts/tagfilter.ts index fcf04f7..d0fcbdf 100644 --- a/util/ts/tagfilter.ts +++ b/util/ts/tagfilter.ts @@ -21,25 +21,42 @@ export class TagFilter { public test(text: string): object { var filtered: object = {}; var reading: string = null; + var filterType: TagFilterType; + var ticks = {}; let words = text.trim().split(/\s+/g); let word: string; for (word of words) { if (word.startsWith('-') && word.length > 1 && this.triggers.has(word.trim())) { - reading = this.filterTypes.get(this.triggers.get(word.trim())) == "toggle" ? null : word.trim(); + filterType = this.filterTypes.get(this.triggers.get(word.trim())); + reading = !['append', 'listAppend'].includes(filterType) ? null : word.trim(); if (!reading) {filtered[`${this.triggers.get(word.trim())}`] = true;} - else {filtered[`${this.triggers.get(reading)}`] = '';} + else {filtered[`${this.triggers.get(reading)}`] = filterType == 'append' ? '' : Array.isArray(filtered[`${this.triggers.get(reading)}`]) ? filtered[`${this.triggers.get(reading)}`] : [];} + if (filterType == "listAppend") { + if (ticks[`${this.triggers.get(word.trim())}`] && ticks[`${this.triggers.get(word.trim())}`].length) {filtered[`${this.triggers.get(word.trim())}`].push(ticks[`${this.triggers.get(word.trim())}`]);} + ticks[`${this.triggers.get(word.trim())}`] = ''; + } } else if (reading) { - filtered[`${this.triggers.get(reading)}`] = `${filtered[`${this.triggers.get(reading)}`]} ${word}`; + if (filterType == "listAppend") {ticks[`${this.triggers.get(reading)}`] += ` ${word}`;} + else {filtered[`${this.triggers.get(reading)}`] = `${filtered[`${this.triggers.get(reading)}`]} ${word}`;} } } + + let tick: string; for (tick of Object.keys(ticks)) { + if (ticks[tick].length) {filtered[tick].push(ticks[tick]);} + } let key: string; for (key of Object.keys(filtered)) { if (typeof filtered[key] == 'string') {filtered[key] = filtered[key].trim();} + else if (Array.isArray(filtered[key])) { + let subkey: string; for (subkey of filtered[key]) { + filtered[key][filtered[key].indexOf(subkey)] = subkey.trim(); + } + } } return filtered; }; } -type TagFilterType = "append" | "toggle"; \ No newline at end of file +type TagFilterType = "append" | "toggle" | "listAppend"; \ No newline at end of file From 50aa98e5d74fc9ba8f93b87d8ddc090bf821b151 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 23 Oct 2020 22:58:58 -0600 Subject: [PATCH 04/41] Begin work on anime command; added anime schema --- commands/anime.js | 32 ++++++++++++++++++++++++++++++++ models/anime.js | 7 ++++++- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 commands/anime.js diff --git a/commands/anime.js b/commands/anime.js new file mode 100644 index 0000000..c4ccd8e --- /dev/null +++ b/commands/anime.js @@ -0,0 +1,32 @@ +import {TagFilter} from "../util/tagfilter"; +import {Tag} from "../util/tag"; + +const Discord = require('discord.js'); +const UserData = require('../models/user'); +const AniData = require('../models/anime'); + +module.exports = { + name: "anime", + aliases: ['ani', 'an'], + help: new Discord.MessageEmbed() + .setTitle("Help -> Anime") + .setDescription("View and find anime in our huge list of anime!") + .addField("Syntax", "`anime <>`"), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}anime <>\``);} + let queue = false; + if (['a', 'add', 'n', 'new'].includes(args[0])) { + let tu = await UserData.findOne({uid: message.author.id}); + if (!tu || tu.staff) { + await message.channel.send("Since you aren't a Natsuki Staff member, this anime will be __submitted__ for reviewal!"); + queue = true; + } + let options = new TagFilter([ + new Tag(['ask', 'question'], 'ask', 'toggle'), + new Tag(['title', 't', 'name', 'n'], 'name', 'append'), + new Tag(['description', 'desc', 'd', 'plot', 'p'], 'plot', 'append') + //new Tag(['']) + ]); + } + } +}; \ No newline at end of file diff --git a/models/anime.js b/models/anime.js index 6af1ed6..bab2440 100644 --- a/models/anime.js +++ b/models/anime.js @@ -3,6 +3,7 @@ const mongoose = require('mongoose'); const AniSchema = new mongoose.Schema({ id: {type: String, unique: true}, name: String, + japname: String, plot: String, publishers: [String], studio: [String], @@ -14,7 +15,11 @@ const AniSchema = new mongoose.Schema({ genres: [String], tags: [String], characters: [String], - streamAt: [String] + streamAt: [String], + watchers: Number, + listed: Number, + liked: Number, + rating: Number }); module.exports = mongoose.model('anime', AniSchema); \ No newline at end of file From 42a938717b1c4d19f5942dbd2c8066b76be58780 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 24 Oct 2020 04:35:05 -0600 Subject: [PATCH 05/41] Finish initial test case for n?ani --- commands/anime.js | 48 +++++++++++++++++++++---- eee | 1 - models/anime.js | 5 +-- util/pagination.d.ts | 33 +++++++++++++++++ util/pagination.js | 86 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 9 deletions(-) delete mode 100644 eee create mode 100644 util/pagination.d.ts create mode 100644 util/pagination.js diff --git a/commands/anime.js b/commands/anime.js index c4ccd8e..ad9edf0 100644 --- a/commands/anime.js +++ b/commands/anime.js @@ -1,5 +1,5 @@ -import {TagFilter} from "../util/tagfilter"; -import {Tag} from "../util/tag"; +const {TagFilter} = require("../util/tagfilter"); +const {Tag} = require ("../util/tag"); const Discord = require('discord.js'); const UserData = require('../models/user'); @@ -17,16 +17,52 @@ module.exports = { let queue = false; if (['a', 'add', 'n', 'new'].includes(args[0])) { let tu = await UserData.findOne({uid: message.author.id}); - if (!tu || tu.staff) { + if (!tu || !tu.staff) { await message.channel.send("Since you aren't a Natsuki Staff member, this anime will be __submitted__ for reviewal!"); queue = true; } let options = new TagFilter([ new Tag(['ask', 'question'], 'ask', 'toggle'), new Tag(['title', 't', 'name', 'n'], 'name', 'append'), - new Tag(['description', 'desc', 'd', 'plot', 'p'], 'plot', 'append') - //new Tag(['']) - ]); + new Tag(['japname', 'japanesename', 'jn'], 'japname', 'listAppend'), + new Tag(['description', 'desc', 'd', 'plot', 'p'], 'plot', 'append'), + new Tag(['pub', 'pubs', 'publishers', 'publisher', 'pb'], 'publishers', 'listAppend'), + new Tag(['stud', 's', 'studio', 'studs', 'studios'], 'studios', 'listAppend'), + new Tag(['began', 'airstart', 'as'], 'airStartDate', 'append'), + new Tag(['ended', 'airend', 'ae'], 'airEndDate', 'append'), + new Tag(['iscomplete', 'completed', 'ic'], 'isComplete', 'toggle'), + new Tag(['seasons', 'sns'], 'seasons', 'append'), + new Tag(['episodes', 'es'], 'episodes', 'append'), + new Tag(['genres', 'g'], 'genres', 'listAppend'), + new Tag(['tags', 'ta', 'tgs', 'tg', 'tag'], 'tags', 'listAppend'), + new Tag(['cs', 'characters', 'chars', 'chs'], 'characters', 'listAppend'), + new Tag(['streams', 'streamat', 'sa'], 'streamAt', 'listAppend') + ]).test(args.join(' ')); + + var foptions = {}; + let option; for (option of Object.keys(options)) { + if (Array.isArray(options[option])) { + let s = ''; + let data; + for (data of options[option]) { + s += data; + s += options[option].indexOf(data) < (options[option].length - 1) ? ', ' : ''; + } + foptions[option] = s; + } + } + + message.channel.send(new Discord.MessageEmbed() + .setTitle(`New Anime -> ${options.name}`) + .setDescription(`${queue ? 'Requested' : 'Added'} by ${message.guild ? message.member.displayName : message.author.username}`) + .addField('Info', `**Name:** ${options.name}\n**Japanese Name:** ${options.japname}\n\n**Publishers:** ${foptions.publishers}\n**Studios:** ${foptions.studios}`) + .addField('Length', `**# of Seasons:** ${options.seasons}\n**# of Episodes:** ${options.episodes}`) + .addField('Airing', `**Began:** ${options.airStartDate}\n**Ended:** ${options.isComplete ? options.airEndDate : 'This anime is still airing!'}`) + .addField('Other', `**Genre(s):** ${foptions.genres}\n**Tags:** ${foptions.tags}\n**Characters:** ${foptions.characters}\n**Stream this at:** ${foptions.streamAt}`) + .setColor("c375f0") + .setFooter('Natsuki', client.user.avatarURL()) + .setTimestamp() + ); } } }; \ No newline at end of file diff --git a/eee b/eee deleted file mode 100644 index 038d718..0000000 --- a/eee +++ /dev/null @@ -1 +0,0 @@ -testing diff --git a/models/anime.js b/models/anime.js index bab2440..44226e9 100644 --- a/models/anime.js +++ b/models/anime.js @@ -6,7 +6,7 @@ const AniSchema = new mongoose.Schema({ japname: String, plot: String, publishers: [String], - studio: [String], + studios: [String], airStartDate: Date, airEndDate: Date, isComplete: Boolean, @@ -19,7 +19,8 @@ const AniSchema = new mongoose.Schema({ watchers: Number, listed: Number, liked: Number, - rating: Number + rating: Number, + lastUpdate: Date }); module.exports = mongoose.model('anime', AniSchema); \ No newline at end of file diff --git a/util/pagination.d.ts b/util/pagination.d.ts new file mode 100644 index 0000000..8517714 --- /dev/null +++ b/util/pagination.d.ts @@ -0,0 +1,33 @@ +import { MessageEmbed, Message, Client } from 'discord.js'; +export declare class Pagination { + title: string; + pages: Page[]; + zeroPage: Page | MessageEmbed; + pageTemplate: MessageEmbed; + message: Message; + timeout: Number; + description: string; + activationMessage: Message; + client: Client; + currentpos: number; + constructor(title: string, pages: Page[], zeroPage: Page | MessageEmbed, client: Client, message: Message, activationMessage: Message, timeout: number, description?: string, pageTemplate?: MessageEmbed); + addPage(page: Page): Pagination; + render(pos: number): Pagination; + nextPage(): Pagination; + prevPage(): Pagination; + destroy(delmsg?: boolean, fmsg?: Message): Pagination; + resetTimeout(newTimeout?: number): Pagination; + init(): Pagination; +} +export declare class Page { + items: PageItem[]; + title: string; + description: string; + constructor(title: string, items: PageItem[], description?: string); + addItem(item: PageItem): Page; +} +interface PageItem { + title: string; + text: string; +} +export {}; diff --git a/util/pagination.js b/util/pagination.js new file mode 100644 index 0000000..bbb8044 --- /dev/null +++ b/util/pagination.js @@ -0,0 +1,86 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Page = exports.Pagination = void 0; +const discord_js_1 = require("discord.js"); +class Pagination { + constructor(title, pages, zeroPage, client, message, activationMessage, timeout, description, pageTemplate) { + this.currentpos = 0; + this.title = title; + this.pages = pages; + this.zeroPage = zeroPage; + this.message = message; + this.timeout = timeout; + this.activationMessage = activationMessage; + this.client = client; + this.description = description ? description : `Requested by ${activationMessage.guild ? activationMessage.member.displayName : activationMessage.author.username}.`; + this.pageTemplate = pageTemplate + ? pageTemplate + : new discord_js_1.MessageEmbed() + .setDescription(this.description) + .addField('Navigation', `Click or tap the arrows below this message to navigate through the pages!\n\n*This menu will timeout in ${this.timeout}ms.`) + .setColor('c375f0') + .setFooter('Natsuki', this.client.user.avatarURL()) + .setTimestamp(); + } + ; + addPage(page) { + this.pages.push(page); + return this; + } + ; + render(pos) { + let page = this.pages[this.currentpos]; + let pageEmbed = new discord_js_1.MessageEmbed() + .setTitle(`${this.title} -> ${page.title}`) + .setDescription(`${this.pageTemplate.description ? this.pageTemplate.description : this.description}\n\n${page.description}`) + .setColor(this.pageTemplate.hexColor ? this.pageTemplate.hexColor : 'c375f0') + .setFooter(this.pageTemplate.footer ? `${this.pageTemplate.footer.text} | Page ${this.currentpos + 1} of ${this.pages.length}` : `Natsuki | Page ${this.currentpos + 1} of ${this.pages.length}`) + .setTimestamp(); + let item; + for (item of page.items) { + pageEmbed.addField(item.title, item.text); + } + if (this.pageTemplate.thumbnail) { + pageEmbed.setThumbnail(this.pageTemplate.thumbnail.url); + } + this.message.edit(pageEmbed); + return this; + } + ; + nextPage() { + return this.render(this.currentpos < (this.pages.length - 1) ? this.currentpos + 1 : this.currentpos); + } + ; + prevPage() { + return this.render(this.currentpos > 0 ? this.currentpos - 1 : this.currentpos); + } + ; + destroy(delmsg, fmsg) { + return this; + } + ; + resetTimeout(newTimeout) { + return this; + } + ; + init() { + return this; + } + ; +} +exports.Pagination = Pagination; +class Page { + constructor(title, items, description) { + this.items = []; + this.title = title; + this.items = items; + this.description = description; + } + ; + addItem(item) { + this.items.push(item); + return this; + } + ; +} +exports.Page = Page; From 4a8e76dc7fd43de3610523bf0ab384d0fbfb44fe Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 24 Oct 2020 12:38:11 -0600 Subject: [PATCH 06/41] StarBoard command finished; Util ask.js for simple message collectors. --- commands/starboard.js | 59 +++++++++++++++++++++++++++++++++++++++++++ models/guild.js | 5 +++- util/ask.js | 12 +++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 commands/starboard.js create mode 100644 util/ask.js diff --git a/commands/starboard.js b/commands/starboard.js new file mode 100644 index 0000000..217154d --- /dev/null +++ b/commands/starboard.js @@ -0,0 +1,59 @@ +const Discord = require('discord.js'); +const GuildData = require('../models/guild'); +const ask = require('../util/ask'); + +module.exports = { + name: "starboard", + aliases: ['sb'], + help: new Discord.MessageEmbed() + .setTitle("Help -> StarBoard") + .setDescription("Setup and view information on this server's starboard! This allows messages to be sent to a dedicated channel when they receive a set number of star messages.") + .addField("Syntax", "`starboard `") + .addField("Notice", "You must have the staff-role or be an admin in order to set up or toggle the starboard"), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!message.guild) {return message.reply("You must be in a server in order to use this command.");} + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}starboard \``);} + let tg = await GuildData.findOne({gid: message.guild.id}) ? await GuildData.findOne({gid: message.guild.id}) : new GuildData({gid: message.guild.id}); + if (['v', 'view', 'c', 'check'].includes(args[0])) {return message.reply(tg.starchannel.length ? tg.starsenabled ? `I'm watching for stars in <#${tg.starchannel}>. Messages will be Starred when they receive ${tg.starreq} :star: reactions.` : "StarBoard has been set up for this server, but isn't enabled at this time." : "StarBoard has not yet been set up in this server. You can do so with `" + prefix + "starboard setup`.");} + if ((tg.staffrole.length && !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.channel.send("You don't have permissions to edit starboard settings in this server!");} + + if (['setup', 's', 'config', 'c'].includes(args[0])) { + if (tg.starchannel.length) {message.channel.send("You already have a starboard set up in your server! This means that by continuing the setup, you'll be overwriting your previous settings.");} + + let ch = await ask(message, 'What channel would you like the starboard to be in? (Time: 30s)', 30000); if (!ch) {return;} + if (ch.match(/^<#(?:\d+)>$/) && message.guild.channels.cache.has(ch.slice(ch.search(/\d/), ch.search('>')))) {tg.starchannel = ch.slice(ch.search(/\d/), ch.search('>'));} + else if (message.guild.channels.cache.has(ch)) {tg.starchannel = ch;} + else {return message.reply("Please specify a channel that actually exists! Try again.");} + + let starNum = await ask(message, 'How many stars should be reacted to a message in order for it to be starred? Please just send a number. (Time: 30s)', 30000); if (!starNum) {return;} + if (isNaN(Number(starNum))) {return message.reply("That isn't a number! Please try again.");} + starNum = Number(starNum); + if (starNum < 3) {return message.reply("You need to have at least 3 stars. Try again");} + if (starNum > 100) {return message.reply("You can't require more than 100 stars! Try again.");} + tg.starreq = starNum; + + tg.starsenabled = true; + tg.save(); + + return message.channel.send(`Got it! I will now be watching for messages with at least ${starNum} reactions, and I'll send them to <#${tg.starchannel}>!`); + } + + else if (['e', 'enable'].includes(args[0]) || (['t', 'toggle'].includes(args[0]) && !tg.starsenabled)) { + if (tg.starsenabled) {return message.reply("StarBoard is already enabled in this server!");} + if (!tg.starchannel.length) {return message.reply(`Please setup StarBoard first! \`${prefix}starboard setup\`.`);} + tg.starsenabled = true; + tg.save(); + return message.channel.send(`I've re-enabled your StarBoard. It's kept the same settings as you had when you disabled it!`); + } + + else if (['d', 'disable'].includes(args[0]) || (['t', 'toggle'].includes(args[0]) && tg.starsenabled)) { + if (!tg.starsenabled) {return message.reply("StarBoard is already disabled in this server!");} + if (!tg.starchannel.length) {return message.reply(`Please setup StarBoard first! \`${prefix}starboard setup\`.`);} + tg.starsenabled = false; + tg.save(); + return message.channel.send(`I've disabled your StarBoard. Your StarBoard configuration will be kept for if/when you re-enable StarBoard.`); + } + + else {return message.reply(`Invalid arg! Syntax: \`${prefix}starboard \``);} + } +}; \ No newline at end of file diff --git a/models/guild.js b/models/guild.js index d8cd5db..a2ca5fd 100644 --- a/models/guild.js +++ b/models/guild.js @@ -15,7 +15,10 @@ const guildSchema = new mongoose.Schema({ levelmessage: {type: String, default: '**{{u}}** has reached level **{{l}}**! :tada:'}, levelmessages: {type: Boolean, default: false}, prefix: {type: String, default: ''}, - nostatus: {type: Boolean, default: false} + nostatus: {type: Boolean, default: false}, + starchannel: {type: String, default: ''}, + starreq: {type: Number, default: 5}, + starsenabled: {type: Boolean, default: false} }); module.exports = mongoose.model("guild", guildSchema); \ No newline at end of file diff --git a/util/ask.js b/util/ask.js new file mode 100644 index 0000000..4151a7c --- /dev/null +++ b/util/ask.js @@ -0,0 +1,12 @@ +module.exports = async (message, toAsk, time) => { + let msg = await message.channel.send(toAsk); + let filter = m => m.author.id === message.author.id; + try { + let collected = await message.channel.awaitMessages(filter, {max: 1, errors: ['time'], time: time}); + collected = collected.first().content; + return collected; + } catch { + message.reply("This question has timed out! Please try again."); + return null; + } +}; \ No newline at end of file From d8d63a3a42ed543b213d8d26d1b219e44efd1a02 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 24 Oct 2020 12:38:43 -0600 Subject: [PATCH 07/41] Bug-fixed staffrole.js --- commands/staffrole.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/staffrole.js b/commands/staffrole.js index 9596626..872c630 100644 --- a/commands/staffrole.js +++ b/commands/staffrole.js @@ -18,7 +18,7 @@ module.exports = { if (['view', 'v'].includes(args[0].trim().toLocaleLowerCase())) {return message.reply( tguild.staffrole.length ? message.guild.roles.cache.has(tguild.staffrole) - ? `\`people with the ${message.guild.roles.cache.get(tguild.staffrole).name}\` role can edit my setting here.` + ? `people with the \`${message.guild.roles.cache.get(tguild.staffrole).name}\` role can edit my setting here.` : `I have a role stored for this server, but it doesn't seem to exist anymore, so only admins can edit my settings right now.` : 'only admins may edit settings in this server.' );} From cbd0bc3c42b0c009220f4b6b1bb02363afc7664d Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 24 Oct 2020 14:31:18 -0600 Subject: [PATCH 08/41] Finished the starboard completely and added star schema --- events/messageReactionAdd.js | 41 ++++++++++++++++++++++++++++++++++++ models/starboard.js | 10 +++++++++ 2 files changed, 51 insertions(+) create mode 100644 events/messageReactionAdd.js create mode 100644 models/starboard.js diff --git a/events/messageReactionAdd.js b/events/messageReactionAdd.js new file mode 100644 index 0000000..8187c5b --- /dev/null +++ b/events/messageReactionAdd.js @@ -0,0 +1,41 @@ +const Discord = require("discord.js"); +const GuildData = require('../models/guild'); +const StarData = require('../models/starboard'); + +module.exports = async (client, reaction, user) => { + if (reaction.partial) {try {await reaction.fetch();} catch {return;}} + + if (reaction.emoji.name === "⭐") { + if (!reaction.message.guild) {return;} + let tg = await GuildData.findOne({gid: reaction.message.guild.id}); + if (!tg) {return;} + if (tg.starchannel.length && tg.starsenabled && reaction.message.guild.channels.cache.has(tg.starchannel) && reaction.message.guild.channels.cache.get(tg.starchannel).permissionsFor(client.user.id).has('SEND_MESSAGES')) { + if (reaction.message.channel.id === tg.starchannel) {return;} + let sd = await StarData.findOne({gid: reaction.message.guild.id}) ? await StarData.findOne({gid: reaction.message.guild.id}) : new StarData({gid: reaction.message.guild.id}); + + let starEmbed = new Discord.MessageEmbed() + .setTitle('Starred Message!') + .setDescription(`Sent by ${reaction.message.member.displayName} (<@${reaction.message.author.id}>) || Channel: ${reaction.message.channel.name} (<#${reaction.message.channel.id}>)\n[Jump to Message](${reaction.message.url})`) + .setThumbnail(reaction.message.author.avatarURL({size: 2048})) + .setColor('ebb931') + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp(); + if (reaction.message.content.length) {starEmbed.addField("Message", reaction.message.content);} + starEmbed + .addField("Stars", `:star: ${reaction.count}`, true) + .addField(`${reaction.message.member.displayName.toLowerCase().endsWith('s') ? `${reaction.message.member.displayName}'` : `${reaction.message.member.displayName}'s`} StarBoard Count`, sd.starCount[reaction.message.author.id] ? sd.starCount[reaction.message.author.id] + 1 : 1, true); + if (reaction.message.attachments.size) {starEmbed.setImage(reaction.message.attachments.first().url);} + if (Object.keys(sd.stars).includes(reaction.message.id)) { + let starMessage = await reaction.message.guild.channels.cache.get(tg.starchannel).messages.fetch(sd.stars[reaction.message.id]); + if (starMessage) {await starMessage.edit(starEmbed);} + } else { + if (reaction.count < tg.starreq) {return;} + let starEmbedMessage = await reaction.message.guild.channels.cache.get(tg.starchannel).send(starEmbed); + sd.stars[reaction.message.id] = starEmbedMessage.id; + sd.starCount[reaction.message.author.id] = sd.starCount[reaction.message.author.id] ? sd.starCount[reaction.message.author.id] + 1 : 1; + sd.serverStarCount += 1; + sd.save(); + } + } + } +}; \ No newline at end of file diff --git a/models/starboard.js b/models/starboard.js new file mode 100644 index 0000000..df758e5 --- /dev/null +++ b/models/starboard.js @@ -0,0 +1,10 @@ +const mongoose = require('mongoose'); + +const StarSchema = new mongoose.Schema({ + gid: {type: String, unique: true}, + stars: {type: Object, default: {}}, + starCount: {type: Object, default: {}}, + serverStarCount: {type: Number, default: 0} +}); + +module.exports = mongoose.model('stars', StarSchema); \ No newline at end of file From 8a8b7c6babf8d2f7974d71be742b892ae14cfda7 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sun, 25 Oct 2020 22:49:32 -0600 Subject: [PATCH 09/41] Add joinrole command and functionality --- commands/autorole.js | 33 +++++++++++++++++++++++++++++++++ commands/eval.js | 7 +------ events/guildCreate.js | 2 +- events/guildMemberAdd.js | 8 ++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 commands/autorole.js create mode 100644 events/guildMemberAdd.js diff --git a/commands/autorole.js b/commands/autorole.js new file mode 100644 index 0000000..d8ee895 --- /dev/null +++ b/commands/autorole.js @@ -0,0 +1,33 @@ +const Discord = require('discord.js'); +const GuildData = require('../models/guild'); + +module.exports = { + name: "autorole", + aliases: ['joinrole', 'jr'], + help: new Discord.MessageEmbed() + .setTitle("Help -> Auto Role/Join Role") + .setDescription("Set a role to be automatically added to users when they join the server.") + .addField("Syntax", "`autorole `") + .addField('Notice', "This command can only be used by server staff members and admins."), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!message.guild) {return message.reply("This command is only available in servers.");} + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}autorole \``);} + let tg = await GuildData.findOne({gid: message.guild.id}) ? await GuildData.findOne({gid: message.guild.id}) : new GuildData({gid: message.guild.id}); + if (['v', 'view', 'check'].includes(args[0])) {return message.channel.send(tg.joinrole.length && message.guild.roles.cache.has(tg.joinrole) ? `I am currently adding \`${message.guild.roles.cache.get(tg.joinrole).name}\` to new members.` : "At the moment, I'm not adding a role to new members.");} + if ((!tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You don't have the permissions to edit this setting.");} + if (['s', 'set', 'c', 'clear'].includes(args[0])) { + let role = message.mentions.roles.first() ? message.mentions.roles.first().id : args[1] && message.guild.roles.cache.has(args[1]) ? args[1] : ['c', 'clear'].includes(args[0]) ? '' : null; + if (role === null) {return message.reply("That doesn't seem to be a role!");} + tg.joinrole = role; + tg.save(); + return message.channel.send(new Discord.MessageEmbed() + .setTitle("Join Role Updated") + .setThumbnail(message.author.avatarURL({size: 2048})) + .setDescription(`Role: ${tg.joinrole.length ? `<@&${tg.joinrole}>` : "None"}`) + .setColor("c375f0") + .setFooter('Natsuki', client.user.avatarURL()) + .setTimestamp() + ); + } + } +}; \ No newline at end of file diff --git a/commands/eval.js b/commands/eval.js index 2c62738..75ae8bd 100644 --- a/commands/eval.js +++ b/commands/eval.js @@ -1,18 +1,13 @@ const Discord = require('discord.js'); -const util = require('util'); -const moment = require('moment'); -const chalk = require('chalk'); module.exports = { name: 'eval', - aliases: ['ev', ':'], + aliases: ['ev', ':', 'e'], help: "Evaluates raw JavaScript code. *This is a __developer-only__ command.* Usage: `{{p}}eval `", execute(message, msg, args, cmd, prefix, mention, client) { try { if (!client.developers.includes(message.author.id)) return; - let kieran = client.users.cache.get('673477059904929802').tag - if (!args.length) return message.channel.send(`Syntax: \`${prefix}eval \``); const result = new Promise((resolve) => resolve(eval(args.join(' ')))); return result.then((output) => { diff --git a/events/guildCreate.js b/events/guildCreate.js index a4173d4..99b745a 100644 --- a/events/guildCreate.js +++ b/events/guildCreate.js @@ -18,7 +18,7 @@ module.exports = async (client, guild) => { .setAuthor('New Guild Added', guild.owner.avatarURL()) .setTitle(guild.name) .setThumbnail(guild.iconURL({size: 2048})) - .addField('Owner', guild.owner.tag, true) + .addField('Owner', client.users.cache.get(guild.owner.id).tag, true) .addField('Members', guild.members.cache.size) .addField('Position', `Server #${client.guilds.cache.size}`) .setColor('55ff7f') diff --git a/events/guildMemberAdd.js b/events/guildMemberAdd.js new file mode 100644 index 0000000..5eb5b6b --- /dev/null +++ b/events/guildMemberAdd.js @@ -0,0 +1,8 @@ +const GuildData = require('../models/guild'); + +module.exports = async (client, member) => { + let tg = await GuildData.findOne({gid: member.guild.id}); + if (tg && tg.joinrole.length && member.guild.roles.cache.has(tg.joinrole)) { + if (member.guild.members.cache.get(client.user.id).permissions.has("MANAGE_ROLES")) {member.roles.add(tg.joinrole);} + } +}; \ No newline at end of file From 65def95ee0839a8cd5e3ea66bd46e2ed77a447b5 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Tue, 27 Oct 2020 12:07:39 -0600 Subject: [PATCH 10/41] Added prefix message when pinged --- events/message.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/events/message.js b/events/message.js index dd22d9b..772bdcb 100644 --- a/events/message.js +++ b/events/message.js @@ -6,7 +6,7 @@ const UserData = require('../models/user'); module.exports = async (client, message) => { if (message.author.bot) {return undefined;} - if (message.channel.type == 'dm') /*{var dmch = true;} else {var dmch = false};*/ {return undefined;} + if (message.channel.type === 'dm') /*{var dmch = true;} else {var dmch = false};*/ {return undefined;} if (message.channel.type !== 'text' && message.channel.type !== 'dm') {return undefined;} //if (message.channel.type == "text") {if (settings[message.guild.id]) {prefix = settings[message.guild.id].prefix;};}; @@ -24,6 +24,13 @@ module.exports = async (client, message) => { : message.content.slice(3 + client.user.id.length).trim().split(/\s+/g); var cmd = args.shift().toLowerCase().trim(); + if ([`<@${client.user.id}>`, `<@!${client.user.id}>`].includes(msg)) { + return message.channel.send(new Discord.MessageEmbed() + .setTitle(["Yep, that's me!", "^^ Hiya!", "Oh, hi there!", "Sure, what's up?", "How can I help!", "Natsuki is busy, but I can take a message for you!", "Teehee that's me!", "You were looking for Natsuki Tivastl, right?", "Sure! What's up?", "Pong!"][Math.floor(Math.random() * 10)]) + .setDescription("My prefix here is `" + prefix + "`. Use `" + prefix + "help` to see what commands you can use.") + .setColor('c375f0')); + } + if (mention && message.guild) {require('../util/mention')(message, msg, args, cmd, prefix, mention, client);} let tu = await UserData.findOne({uid: message.author.id}); if (tu && tu.statusmsg.length && tu.statusclearmode === 'auto') { From 5da56dfa60eabc3e16c0fa31f26e910363237fee Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Wed, 28 Oct 2020 13:41:31 -0600 Subject: [PATCH 11/41] Add response parsing and sending; response command with 'quick' arg --- commands/response.js | 23 ++++++++++++++++++ util/parseresponse.js | 55 +++++++++++++++++++++++++++++++++++++++++++ util/sendresponse.js | 20 ++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 commands/response.js create mode 100644 util/parseresponse.js create mode 100644 util/sendresponse.js diff --git a/commands/response.js b/commands/response.js new file mode 100644 index 0000000..f97a01a --- /dev/null +++ b/commands/response.js @@ -0,0 +1,23 @@ +const Discord = require('discord.js'); +const GuildData = require('../models/guild'); + +const sendResponse = require('../util/sendresponse'); +const parseResponse = require('../util/parseresponse'); + +module.exports = { + name: "response", + aliases: ['r', 'resp'], + help: new Discord.MessageEmbed() + .setTitle("Help -> Responses") + .setDescription("Configure your server's saved responses. These are reusable and editable, and can be placed in things like welcome messages and used for announcements.") + .addField("Syntax", "`response `") + .addField("Notice", "You must have your server's staff role or be an admin to use this command."), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!message.guild) {return message.reply("You must be in a server to use this command.");} + let tg = await GuildData.findOne({gid: message.guild.id}); + if (!tg && !['q', 'quick'].includes(args[0].toLowerCase()) && (tg.staffrole.length && !message.member.roles.cache.has(tg.staffrole)) && message.member.permissions.has("ADMINISTRATOR")) {return message.reply("you need to be staff or admin in this server in order to edit those settings.");} + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}response \``);} + + if (['q', 'quick'].includes(args[0].toLowerCase())) {return sendResponse(message.channel, 'quick', client, await parseResponse(message, client, args));} + } +}; \ No newline at end of file diff --git a/util/parseresponse.js b/util/parseresponse.js new file mode 100644 index 0000000..0c4deed --- /dev/null +++ b/util/parseresponse.js @@ -0,0 +1,55 @@ +const {Tag} = require('./tag'); +const {TagFilter} = require('./tagfilter'); + +module.exports = async (message, client, args) => { + let options = new TagFilter([ + new Tag(['em', '-embed'], 'embed', 'toggle'), + new Tag(['-msg', 'message'], 'message', 'toggle'), + + new Tag(['name', 'n'], 'name', 'append'), + new Tag(['ch', 'channel'], 'channel', 'append'), + + new Tag(['text', 'txt'], 'text', 'append'), + + new Tag(['title', 't'], 'title', 'append'), + new Tag(['description', 'desc', 'd'], 'description', 'append'), + new Tag(['fieldname', 'fn', 'newfield', 'nf'], 'fieldnames', 'listAppend'), + new Tag(['fieldtext', 'ft', 'fieldcontent', 'fc'], 'fieldtexts', 'listAppend'), + new Tag(['image', 'i'], 'image', 'append'), + new Tag(['thumbnail', 'thumb', 'th'], 'thumbnail', 'append'), + new Tag(['servericonthumbnail', 'serverthumbnail', 'sit', 'st'], 'guildthumb', 'toggle'), + new Tag(['servericonimage', 'serverimage', 'sii', 'si'], 'guildimage', 'toggle'), + new Tag(['color', 'colour', 'col', 'c'], 'color', 'append'), + ]).test(args.join(" ")); + + if (options.fieldnames && options.fieldnames.length) { + if (!options.fieldtexts || !options.fieldtexts.length || options.fieldnames.length !== options.fieldtexts.length) { + message.reply("You must have the same amount of field names as you do field texts."); return null; + } + } + if (options.embed) { + if (options.fieldnames && options.fieldnames.length > 10) {message.reply("You can't have more than 10 fields!"); return null;} + if (options.color && options.color.length && (![3, 6].includes(options.color.length))) {message.reply("Your color must be a hex value 3 or 6 digits long."); return null;} + if (options.title && options.title.length > 65) {message.reply("Your title should be less than 65 characters, please :)"); return null;} + if (options.description && options.description.length > 750) {message.reply("Your description should be less than 750 characters."); return null;} + if ((!options.title || !options.title.length) || (!options.description || !options.description.length)) {message.reply("You need have a title and a description!"); return null;} + if (options.image && options.image.length > 300) {message.reply("Your image URL is a bit too long. Try shortening the URL or hosting it somewhere like imgur."); return null;} + if (options.thumbnail && options.image.thumbnail > 300) {message.reply("Your thumbnail URL is a bit too long. Try shortening the URL or hosting it somewhere like imgur."); return null;} + if (options.fieldnames) { + let fn; let ft; + for (fn of options.fieldnames) { + if (fn.length > 65) {message.reply("One of your field names is longer than 65 characters. Please shorten it!"); return null;} + } for (ft of options.fieldtexts) { + if (ft.length > 500) {message.reply("One of your field texts is longer than 500 characters. Please shorten it!"); return null;} + } + } + if (options.guildthumb) {options.thumbnail = message.guild.iconURL({size: 2048});} + if (options.guildimage) {options.image = message.guild.iconURL({size: 2048});} + } else if (options.message) { + if (options.text && options.text.length > 750) {message.reply("Please keep your message text under 750 characters!"); return null;} + } else {message.reply("You must specify either '-message' or '-embed' for the format of your response."); return null;} + + if (options.channel && options.channel.length) {if (!options.channel.match(/^<#(?:\d+)>$/) && !message.guild.channels.cache.has(options.channel.slice(options.channel.search(/\d/), options.channel.search(">")))) {message.reply("You must use a valid channel in this server."); return null;}} + + return options; +}; \ No newline at end of file diff --git a/util/sendresponse.js b/util/sendresponse.js new file mode 100644 index 0000000..9bd0ac7 --- /dev/null +++ b/util/sendresponse.js @@ -0,0 +1,20 @@ +const Discord = require('discord.js'); + +module.exports = async(channel, mode, client, options) => { + if (!options) {return;} + if (options.channel && options.channel.length) {channel = channel.guild.channels.cache.get(options.channel.slice(options.channel.search(/\d/), options.channel.search('>')));} + try { + if (mode === "welcome") {} else if (mode === "leave") {} else { + if (options.embed) { + var responseEmbed = new Discord.MessageEmbed().setTitle(options.title).setDescription(options.description); + if (options.fieldnames && options.fieldnames.length) {let i; for (i=0;i Date: Wed, 28 Oct 2020 15:28:27 -0600 Subject: [PATCH 12/41] Response saving, retrieving, and sending --- commands/response.js | 12 +++++++++--- models/responses.js | 9 +++++++++ util/response/getresponse.js | 9 +++++++++ util/{ => response}/parseresponse.js | 10 ++++++++-- util/response/saveresponse.js | 15 +++++++++++++++ util/{ => response}/sendresponse.js | 0 6 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 models/responses.js create mode 100644 util/response/getresponse.js rename util/{ => response}/parseresponse.js (89%) create mode 100644 util/response/saveresponse.js rename util/{ => response}/sendresponse.js (100%) diff --git a/commands/response.js b/commands/response.js index f97a01a..cab0c61 100644 --- a/commands/response.js +++ b/commands/response.js @@ -1,8 +1,10 @@ const Discord = require('discord.js'); const GuildData = require('../models/guild'); -const sendResponse = require('../util/sendresponse'); -const parseResponse = require('../util/parseresponse'); +const sendResponse = require('../util/response/sendresponse'); +const parseResponse = require('../util/response/parseresponse'); +const saveResponse = require('../util/response/saveresponse'); +const getResponse = require('../util/response/getresponse'); module.exports = { name: "response", @@ -18,6 +20,10 @@ module.exports = { if (!tg && !['q', 'quick'].includes(args[0].toLowerCase()) && (tg.staffrole.length && !message.member.roles.cache.has(tg.staffrole)) && message.member.permissions.has("ADMINISTRATOR")) {return message.reply("you need to be staff or admin in this server in order to edit those settings.");} if (!args.length) {return message.channel.send(`Syntax: \`${prefix}response \``);} - if (['q', 'quick'].includes(args[0].toLowerCase())) {return sendResponse(message.channel, 'quick', client, await parseResponse(message, client, args));} + if (args.length < 1) {return message.reply("You have to tell me what I'm supposed to find or save!");} + + if (['q', 'quick'].includes(args[0].toLowerCase())) {return await sendResponse(message.channel, 'quick', client, await parseResponse(message, client, args));} + if (['n', 'new', 's', 'save'].includes(args[0].toLowerCase())) {return await saveResponse(await parseResponse(message, client, args), message);} + if (['t', 'test', 'send'].includes(args[0].toLowerCase())) {return await sendResponse(message.channel, 'quick', client, await getResponse(message, args[1]));} } }; \ No newline at end of file diff --git a/models/responses.js b/models/responses.js new file mode 100644 index 0000000..4f83bbf --- /dev/null +++ b/models/responses.js @@ -0,0 +1,9 @@ +const mongoose = require('mongoose'); + +const ResponseSchema = new mongoose.Schema({ + gid: {type: String, unique: true}, + responses: {type: Map, default: new Map()}, + bindings: {type: Map, default: new Map()} +}); + +module.exports = mongoose.model('responses', ResponseSchema); \ No newline at end of file diff --git a/util/response/getresponse.js b/util/response/getresponse.js new file mode 100644 index 0000000..55bc7f0 --- /dev/null +++ b/util/response/getresponse.js @@ -0,0 +1,9 @@ +const Responses = require('../../models/responses'); + +module.exports = async (message, name) => { + let tr = await Responses.findOne({gid: message.guild.id}); + if (!tr) {message.reply("This server does not have any responses saved!"); return null;} + if (!tr.responses.has(name.toLowerCase())) {message.reply("I don't have that response saved here."); return null;} + message.delete(); + return tr.responses.get(name.toLowerCase()); +}; \ No newline at end of file diff --git a/util/parseresponse.js b/util/response/parseresponse.js similarity index 89% rename from util/parseresponse.js rename to util/response/parseresponse.js index 0c4deed..4f91600 100644 --- a/util/parseresponse.js +++ b/util/response/parseresponse.js @@ -1,5 +1,5 @@ -const {Tag} = require('./tag'); -const {TagFilter} = require('./tagfilter'); +const {Tag} = require('../tag'); +const {TagFilter} = require('../tagfilter'); module.exports = async (message, client, args) => { let options = new TagFilter([ @@ -51,5 +51,11 @@ module.exports = async (message, client, args) => { if (options.channel && options.channel.length) {if (!options.channel.match(/^<#(?:\d+)>$/) && !message.guild.channels.cache.has(options.channel.slice(options.channel.search(/\d/), options.channel.search(">")))) {message.reply("You must use a valid channel in this server."); return null;}} + if (options.name && options.name.length) { + options.name = options.name.toLowerCase(); + if (options.name.length > 10) {message.reply("The option name must be less than 10 characters."); return null;} + if (!options.name.match(/^[a-z0-9-_]+$/)) {message.reply("You can only use a-z, numbers, hyphens, and underscores."); return null;} + } + return options; }; \ No newline at end of file diff --git a/util/response/saveresponse.js b/util/response/saveresponse.js new file mode 100644 index 0000000..f983666 --- /dev/null +++ b/util/response/saveresponse.js @@ -0,0 +1,15 @@ +const Responses = require('../../models/responses'); + +module.exports = async (options, message) => { + try { + if (!options) {return null;} + if (!options.name || !options.name.length) {message.reply("You need to have a name in order to save a response."); return null;} + let sr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id}); + if (sr.responses.has(options.name)) {message.reply("You already have a response with that name. Use `edit` instead."); return null;} + sr.responses.set(options.name, options); + sr.save(); + message.channel.send("Response added!"); + } catch {message.reply("There seems to have been an error in saving your response. If this persists, please contact the developers or join the support sever."); return null;} + + return options; +}; \ No newline at end of file diff --git a/util/sendresponse.js b/util/response/sendresponse.js similarity index 100% rename from util/sendresponse.js rename to util/response/sendresponse.js From 0fbe54a598b82a15afe0271ebeeed8002c99e2cc Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Thu, 29 Oct 2020 01:10:28 -0600 Subject: [PATCH 13/41] Finish text filtering and add some filters --- commands/response.js | 4 ++-- events/message.js | 1 - util/response/filterresponse.js | 7 +++++++ util/response/sendresponse.js | 10 ++++++---- 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 util/response/filterresponse.js diff --git a/commands/response.js b/commands/response.js index cab0c61..5bddf7c 100644 --- a/commands/response.js +++ b/commands/response.js @@ -22,8 +22,8 @@ module.exports = { if (args.length < 1) {return message.reply("You have to tell me what I'm supposed to find or save!");} - if (['q', 'quick'].includes(args[0].toLowerCase())) {return await sendResponse(message.channel, 'quick', client, await parseResponse(message, client, args));} + if (['q', 'quick'].includes(args[0].toLowerCase())) {return await sendResponse(message, message.channel, 'quick', client, await parseResponse(message, client, args));} if (['n', 'new', 's', 'save'].includes(args[0].toLowerCase())) {return await saveResponse(await parseResponse(message, client, args), message);} - if (['t', 'test', 'send'].includes(args[0].toLowerCase())) {return await sendResponse(message.channel, 'quick', client, await getResponse(message, args[1]));} + if (['t', 'test', 'send'].includes(args[0].toLowerCase())) {return await sendResponse(message, message.channel, 'quick', client, await getResponse(message, args[1]));} } }; \ No newline at end of file diff --git a/events/message.js b/events/message.js index 772bdcb..e3b5939 100644 --- a/events/message.js +++ b/events/message.js @@ -6,7 +6,6 @@ const UserData = require('../models/user'); module.exports = async (client, message) => { if (message.author.bot) {return undefined;} - if (message.channel.type === 'dm') /*{var dmch = true;} else {var dmch = false};*/ {return undefined;} if (message.channel.type !== 'text' && message.channel.type !== 'dm') {return undefined;} //if (message.channel.type == "text") {if (settings[message.guild.id]) {prefix = settings[message.guild.id].prefix;};}; diff --git a/util/response/filterresponse.js b/util/response/filterresponse.js new file mode 100644 index 0000000..6501ea3 --- /dev/null +++ b/util/response/filterresponse.js @@ -0,0 +1,7 @@ +module.exports = async (member, client, text) => { + text = text + .replace(/(?:{{member}}|{{m}})/gm, member.displayName) + .replace(/(?:{{membercount}}|{{mc}})/gm, `${member.guild.members.cache.size}`) + .replace(/(?:{{owner}}|{{o}})/gm, member.guild.owner.displayName); + return text; +}; \ No newline at end of file diff --git a/util/response/sendresponse.js b/util/response/sendresponse.js index 9bd0ac7..a22d597 100644 --- a/util/response/sendresponse.js +++ b/util/response/sendresponse.js @@ -1,19 +1,21 @@ const Discord = require('discord.js'); -module.exports = async(channel, mode, client, options) => { +const filterResponse = require('./filterresponse'); + +module.exports = async(message, channel, mode, client, options) => { if (!options) {return;} if (options.channel && options.channel.length) {channel = channel.guild.channels.cache.get(options.channel.slice(options.channel.search(/\d/), options.channel.search('>')));} try { if (mode === "welcome") {} else if (mode === "leave") {} else { if (options.embed) { - var responseEmbed = new Discord.MessageEmbed().setTitle(options.title).setDescription(options.description); - if (options.fieldnames && options.fieldnames.length) {let i; for (i=0;i Date: Fri, 30 Oct 2020 04:03:05 -0600 Subject: [PATCH 14/41] tidy bugs and add response deletion and bindings deletion detection --- commands/response.js | 22 +++++++++++++-- commands/welcome.js | 49 ++++++++++++++++++++++++++++++++++ events/guildMemberAdd.js | 10 +++++++ util/response/parseresponse.js | 1 + util/response/sendresponse.js | 22 +++++++-------- 5 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 commands/welcome.js diff --git a/commands/response.js b/commands/response.js index 5bddf7c..b635930 100644 --- a/commands/response.js +++ b/commands/response.js @@ -1,5 +1,6 @@ const Discord = require('discord.js'); const GuildData = require('../models/guild'); +const Responses = require('../models/responses'); const sendResponse = require('../util/response/sendresponse'); const parseResponse = require('../util/response/parseresponse'); @@ -22,8 +23,25 @@ module.exports = { if (args.length < 1) {return message.reply("You have to tell me what I'm supposed to find or save!");} - if (['q', 'quick'].includes(args[0].toLowerCase())) {return await sendResponse(message, message.channel, 'quick', client, await parseResponse(message, client, args));} + if (['q', 'quick'].includes(args[0].toLowerCase())) {return await sendResponse(message.member, message.channel, 'quick', client, await parseResponse(message, client, args));} if (['n', 'new', 's', 'save'].includes(args[0].toLowerCase())) {return await saveResponse(await parseResponse(message, client, args), message);} - if (['t', 'test', 'send'].includes(args[0].toLowerCase())) {return await sendResponse(message, message.channel, 'quick', client, await getResponse(message, args[1]));} + if (['t', 'test', 'send'].includes(args[0].toLowerCase())) {return await sendResponse(message.member, message.channel, 'quick', client, await getResponse(message, args[1]));} + if (['r', 'remove', 'd', 'delete', 'del'].includes(args[0].toLowerCase())) { + let tr = await Responses.findOne({gid: message.guild.id}); + if (!tr) {return message.reply("This server has no responses for me to delete.");} + if (!tr.responses.has(args[1].toLowerCase())) {return message.reply("I can't find that response.");} + tr.responses.delete(args[1].toLowerCase()); + let hadBinding = false; + let bm = ''; + tr.bindings.forEach((v, k) => {if (v === args[1].toLowerCase()) { + tr.bindings.delete(v); + hadBinding = true; + bm += `This response was bound to \`${k}\`, so that has also been removed.\n`; + }}); + tr.save(); + return message.channel.send(`I removed the response \`${args[1].toLowerCase()}\`.${hadBinding ? `\n\n${bm}` : ''}`); + } + + return message.channel.send(`Syntax: \`${prefix}response \``); } }; \ No newline at end of file diff --git a/commands/welcome.js b/commands/welcome.js new file mode 100644 index 0000000..40a3adb --- /dev/null +++ b/commands/welcome.js @@ -0,0 +1,49 @@ +const Discord = require('discord.js'); +const GuildData = require('../models/guild'); +const Responses = require('../models/responses'); +const sendResponse = require('../util/response/sendresponse'); + +module.exports = { + name: "welcome", + aliases: ['wel', 'welcomemsg', 'welcomemessage', 'welcomechannel', 'wch', 'wmsg', 'welcomech'], + help: new Discord.MessageEmbed() + .setTitle("Help -> Welcome Messages") + .setDescription("Set the channel and message for your welcome messages!") + .addField("Syntax", "`welcome `") + .addField("Notice", "You must be a staff or admin in your server to edit these settings.") + .addField("Responses", "Your welcome message should be generated through a response using my `response` command, and then bound to the welcome message by providing your response's name."), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!message.guild) {return message.reply("This command is server-only.");} + let tg = await GuildData.findOne({gid: message.guild.id}) ? await GuildData.findOne({gid: message.guild.id}) : new GuildData({gid: message.guild.id}); + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}welcome \``);} + if (['v', 'view', 'c', 'check'].includes(args[0].toLowerCase())) {} + if ((!tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You can't do that without staff or admin permissions, silly!");} + + if (['s', 'set'].includes(args[0].toLowerCase())) { + if (!args[1]) {return message.reply("You need to specify a channel for your welcome messages to be sent in!");} + let ch = message.mentions.channels.first() && args[1].match(/^<#(?:\d+)>$/) ? message.mentions.channels.first().id : message.guild.channels.cache.has(args[1]) ? message.guild.channels.cache.get(args[1]).id : null; + if (!ch) {return message.reply("I can't find that channel!");} + if (!message.guild.channels.cache.get(ch).permissionsFor(client.user.id).has("SEND_MESSAGES")) {return message.reply("I can't send messages in that channel. Try fixing the permissions or using a different channel!");} + if (!args[2]) {return message.reply(`You have to specify a response to use! You can make one with \`${prefix}response new\`.`);} + let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id}); + if (!tr.responses.has(args[2].toLowerCase())) {return message.reply("Silly, I can't welcome someone with a response that doesn't exist! Try making one or make sure you spelled the name correctly.");} + tg.wch = ch; + tg.save(); + tr.bindings.set('welcome', args[2].toLowerCase()); + tr.save(); + return message.channel.send(new Discord.MessageEmbed() + .setTitle("Welcome Channel/Message Updated") + .setDescription(`This server's member-welcoming settings have been altered by ${message.author.tag}.\n\n**Channel**: <#${ch}>\n**Response Name**: \`${args[2].toLowerCase()}\``) + .setColor('c375f0') + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp() + ) + } + + if (['t', 'test'].includes(args[0].toLowerCase())) { + let tr = await Responses.findOne({gid: message.guild.id}); + if (!tr || !tr.bindings.has('welcome') || !tr.responses.has(tr.bindings.get('welcome'))) {return message.reply("I can't test your welcome message because the response doesn't exist, a welcome response isn't set, or you haven't made any responses in this server.");} + await sendResponse(message.member, message.channel, 'this shit aint matter anymore lol', client, tr.responses.get(tr.bindings.get('welcome'))); + } + } +}; \ No newline at end of file diff --git a/events/guildMemberAdd.js b/events/guildMemberAdd.js index 5eb5b6b..5985e4f 100644 --- a/events/guildMemberAdd.js +++ b/events/guildMemberAdd.js @@ -1,8 +1,18 @@ const GuildData = require('../models/guild'); +const Responses = require('../models/responses'); +const sendResponse = require('../util/response/sendresponse'); module.exports = async (client, member) => { let tg = await GuildData.findOne({gid: member.guild.id}); + let tr = await Responses.findOne({gid: member.guild.id}); if (tg && tg.joinrole.length && member.guild.roles.cache.has(tg.joinrole)) { if (member.guild.members.cache.get(client.user.id).permissions.has("MANAGE_ROLES")) {member.roles.add(tg.joinrole);} } + if ( + tr && tr.bindings.has('welcome') && tr.responses.has(tr.bindings.get('welcome')) + && tg.wch.length && member.guild.channels.cache.has(tg.wch) + && member.guild.channels.cache.get(tg.wch).permissionsFor(client.user.id).has("SEND_MESSAGES") + ) { + member.guild.channels.cache.get(tg.wch).send(await sendResponse(member, member.guild.channels.cache.get(tg.wch), 'xdlol', client, tr.responses.get(tr.bindings.get('welcome')))); + } }; \ No newline at end of file diff --git a/util/response/parseresponse.js b/util/response/parseresponse.js index 4f91600..5705614 100644 --- a/util/response/parseresponse.js +++ b/util/response/parseresponse.js @@ -47,6 +47,7 @@ module.exports = async (message, client, args) => { if (options.guildimage) {options.image = message.guild.iconURL({size: 2048});} } else if (options.message) { if (options.text && options.text.length > 750) {message.reply("Please keep your message text under 750 characters!"); return null;} + if (!options.text || !options.text.length) {return message.reply("You must specify -text for your message.");} } else {message.reply("You must specify either '-message' or '-embed' for the format of your response."); return null;} if (options.channel && options.channel.length) {if (!options.channel.match(/^<#(?:\d+)>$/) && !message.guild.channels.cache.has(options.channel.slice(options.channel.search(/\d/), options.channel.search(">")))) {message.reply("You must use a valid channel in this server."); return null;}} diff --git a/util/response/sendresponse.js b/util/response/sendresponse.js index a22d597..1b55938 100644 --- a/util/response/sendresponse.js +++ b/util/response/sendresponse.js @@ -2,21 +2,19 @@ const Discord = require('discord.js'); const filterResponse = require('./filterresponse'); -module.exports = async(message, channel, mode, client, options) => { +module.exports = async(member, channel, mode, client, options) => { if (!options) {return;} if (options.channel && options.channel.length) {channel = channel.guild.channels.cache.get(options.channel.slice(options.channel.search(/\d/), options.channel.search('>')));} try { - if (mode === "welcome") {} else if (mode === "leave") {} else { - if (options.embed) { - var responseEmbed = new Discord.MessageEmbed().setTitle(options.title).setDescription(await filterResponse(message.member, client, options.description)); - if (options.fieldnames && options.fieldnames.length) {let i; for (i=0;i Date: Fri, 30 Oct 2020 20:02:12 -0600 Subject: [PATCH 15/41] Added n?bio completely --- commands/bio.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ models/user.js | 4 +++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 commands/bio.js diff --git a/commands/bio.js b/commands/bio.js new file mode 100644 index 0000000..c1324d7 --- /dev/null +++ b/commands/bio.js @@ -0,0 +1,49 @@ +const Discord = require('discord.js'); +const UserData = require('../models/user'); + +module.exports = { + name: "bio", + help: new Discord.MessageEmbed() + .setTitle("Help -> Bio") + .setDescription("Set and view user bios, which are fun ways to express yourself!") + .addField("Syntax", "`bio `"), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}bio \``);} + let tu = await UserData.findOne({uid: message.author.id}) ? await UserData.findOne({uid: message.author.id}) : new UserData({uid: message.author.id}); + + if (['v', 'view', 'check'].includes(args[0].toLowerCase())) { + let person = args[1] ? args[1].match(/<@(?:!?)(?:\d+)>/) && message.mentions.users.first() ? message.mentions.users.first().id : message.guild && message.guild.members.cache.has(args[1]) ? args[1] : message.author.id : message.author.id; + let pud = await UserData.findOne({uid: person}); + if (!pud || !pud.bio || !pud.bio.length) {return message.reply(person === message.author.id ? "You don't have a bio set!" : "That user has no bio for me to show you!");} + return message.channel.send(new Discord.MessageEmbed() + .setTitle(`Bio for ${message.guild ? message.guild.members.cache.get(person).displayName : message.author.username}`) + .setThumbnail(client.users.cache.get(person).avatarURL({size: 2048})) + .setDescription(pud.bio) + .setColor(pud.color && pud.color.length ? pud.color : 'c375f0') + .setFooter('Natsuki', client.user.avatarURL()) + .setTimestamp() + ); + } + if (['s', 'set'].includes(args[0].toLowerCase())) { + args.shift(); + if (!args.length) {return message.reply("Please specify a bio!");} + let bio = args.join(" "); + if (bio.length > 200) {return message.reply("Please keep your bio under 200 characters!");} + tu.bio = bio; + tu.save(); + return message.channel.send(new Discord.MessageEmbed() + .setTitle(`Bio Set!`) + .setThumbnail(message.author.avatarURL({size: 2048})) + .setDescription(tu.bio) + .setColor(tu.color && tu.color.length ? tu.color : 'c375f0') + .setFooter('Natsuki', client.user.avatarURL()) + .setTimestamp() + ); + } + if (['c', 'clear'].includes(args[0].toLowerCase())) { + tu.bio = ''; + tu.save(); + return message.reply("Bio cleared!"); + } + } +}; \ No newline at end of file diff --git a/models/user.js b/models/user.js index 29e5e29..9b6f51c 100644 --- a/models/user.js +++ b/models/user.js @@ -15,7 +15,9 @@ const UserSchema = new mongoose.Schema({ admin: {type: Boolean, default: false}, developer: {type: Boolean, default: false}, blacklisted: {type: Boolean, default: false}, - donator: {type: Boolean, default: false} + donator: {type: Boolean, default: false}, + bio: {type: String, default: ''}, + color: {type: String, default: ''} }); module.exports = mongoose.model("user", UserSchema); \ No newline at end of file From 9def1dc95aad0d98ab1ac791ba2005da95785033 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 30 Oct 2020 20:02:55 -0600 Subject: [PATCH 16/41] A few additions to responses and new response filters --- commands/response.js | 26 ++++++++++++++++++++++++++ util/response/filterresponse.js | 6 +++++- util/ts/pagination.ts | 9 ++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/commands/response.js b/commands/response.js index b635930..cf3d5bb 100644 --- a/commands/response.js +++ b/commands/response.js @@ -41,6 +41,32 @@ module.exports = { tr.save(); return message.channel.send(`I removed the response \`${args[1].toLowerCase()}\`.${hadBinding ? `\n\n${bm}` : ''}`); } + if (['list', 'l'].includes(args[0].toLowerCase())) { + let tr = await Responses.findOne({gid: message.guild.id}); + if (!tr && !tr.responses.size) {return message.reply("This server has no responses for me to show you.");} + let s = "This server's response names: "; let resps = Array.from(tr.responses.keys()); + let resp; for (resp of resps) {s += `\`${resp}\`${resps.indexOf(resp) !== resps.length - 1 ? ', ' : ''}`;} + return message.channel.send(s); + } + if (['view', 'v'].includes(args[0].toLowerCase())) { + let tr = await Responses.findOne({gid: message.guild.id}); + if (!tr) {return message.reply("I'd give you information on a response, but this server doesn't have any.");} + if (!tr.responses.has(args[1].toLowerCase())) {return message.reply("I can't find that response.");} + let hasBinding = false; + let bm = ''; + tr.bindings.forEach((v, k) => {if (v === args[1].toLowerCase()) {hasBinding = true; bm += !bm.length ? `\`${k}\`` : `, \`${k}\``}}); + let infoEmbed = new Discord.MessageEmbed() + .setTitle("Response Info") + .setDescription(`Requested by ${message.member.displayName}`) + .addField("Name/ID", args[1].toLowerCase(), true) + .addField("Type", tr.responses.get(args[1].toLowerCase()).embed ? "Embed" : "Message", true) + .setColor('c375f0') + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp(); + if (hasBinding) {infoEmbed.addField("Server Bindings", bm);} + return message.channel.send(infoEmbed); + } + return message.channel.send(`Syntax: \`${prefix}response \``); } diff --git a/util/response/filterresponse.js b/util/response/filterresponse.js index 6501ea3..3813574 100644 --- a/util/response/filterresponse.js +++ b/util/response/filterresponse.js @@ -2,6 +2,10 @@ module.exports = async (member, client, text) => { text = text .replace(/(?:{{member}}|{{m}})/gm, member.displayName) .replace(/(?:{{membercount}}|{{mc}})/gm, `${member.guild.members.cache.size}`) - .replace(/(?:{{owner}}|{{o}})/gm, member.guild.owner.displayName); + .replace(/(?:{{owner}}|{{o}})/gm, member.guild.owner.displayName) + .replace(/(?:{{ping}}|{{mp}}|{{memberping}}|{{p}})/gm, `<@${member.id}>`) + .replace(/(?:{{s}}|{{server}}|{{servername}}|{{sn}})/gm, member.guild.name) + .replace(/{{n}}/gm, '\n') + .replace(/{{nn}}/gm, '\n\n'); return text; }; \ No newline at end of file diff --git a/util/ts/pagination.ts b/util/ts/pagination.ts index 3f3ce96..14b1ada 100644 --- a/util/ts/pagination.ts +++ b/util/ts/pagination.ts @@ -18,7 +18,12 @@ export class Pagination { constructor (title: string, pages: Page[], zeroPage: Page | MessageEmbed, client: Client, message: Message, activationMessage: Message, timeout: number, description?: string, pageTemplate?: MessageEmbed) { this.title = title; - this.pages = pages; + + let tpages = []; + tpages.push(zeroPage); + let tpage: Page; for (tpage of pages) {tpages.push(tpage);} + this.pages = tpages; + this.zeroPage = zeroPage; this.message = message; this.timeout = timeout; @@ -77,6 +82,8 @@ export class Pagination { }; public init(): Pagination { + + return this; }; From e0a88ff461e887a4053dc35e644ac402a5fe7090 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 30 Oct 2020 22:46:29 -0600 Subject: [PATCH 17/41] Smol bio bug --- commands/bio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/bio.js b/commands/bio.js index c1324d7..fc8486d 100644 --- a/commands/bio.js +++ b/commands/bio.js @@ -12,7 +12,7 @@ module.exports = { let tu = await UserData.findOne({uid: message.author.id}) ? await UserData.findOne({uid: message.author.id}) : new UserData({uid: message.author.id}); if (['v', 'view', 'check'].includes(args[0].toLowerCase())) { - let person = args[1] ? args[1].match(/<@(?:!?)(?:\d+)>/) && message.mentions.users.first() ? message.mentions.users.first().id : message.guild && message.guild.members.cache.has(args[1]) ? args[1] : message.author.id : message.author.id; + let person = args[1] ? args[1].match(/^<@(?:!?)(?:\d+)>$/) && message.mentions.users.first() ? message.mentions.users.first().id : message.guild && message.guild.members.cache.has(args[1]) ? args[1] : message.author.id : message.author.id; let pud = await UserData.findOne({uid: person}); if (!pud || !pud.bio || !pud.bio.length) {return message.reply(person === message.author.id ? "You don't have a bio set!" : "That user has no bio for me to show you!");} return message.channel.send(new Discord.MessageEmbed() From c9e25c7d99263da2fc5b2e815ea5a5015011d425 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 30 Oct 2020 22:47:04 -0600 Subject: [PATCH 18/41] Added utils and schema for cry/hug/pat-related commands --- bot.js | 2 ++ models/saves.js | 8 ++++++++ util/makeid.js | 10 ++++++++++ 3 files changed, 20 insertions(+) create mode 100644 models/saves.js create mode 100644 util/makeid.js diff --git a/bot.js b/bot.js index 6875b40..cc3127f 100644 --- a/bot.js +++ b/bot.js @@ -13,6 +13,8 @@ async function init() { ['command', 'event'].forEach(x => require(`./handle/${x}`)(client)); client.developers = ["330547934951112705", "673477059904929802"]; + client.misc = {}; + client.misc.savers = ['497598953206841375']; client.utils = {}; client.utils.logch = async () => {return client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915');}; diff --git a/models/saves.js b/models/saves.js new file mode 100644 index 0000000..a15d1dc --- /dev/null +++ b/models/saves.js @@ -0,0 +1,8 @@ +const mongoose = require('mongoose'); + +const SaveSchema = new mongoose.Schema({ + name: {type: String, unique: true}, + saves: {type: Map, default: new Map()} +}); + +module.exports = mongoose.model('saves', SaveSchema); \ No newline at end of file diff --git a/util/makeid.js b/util/makeid.js new file mode 100644 index 0000000..215eaa4 --- /dev/null +++ b/util/makeid.js @@ -0,0 +1,10 @@ +module.exports = (length) => { + let result = ''; + let characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; + let charactersLength = characters.length; + let i; + for (i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} \ No newline at end of file From 379dce8547936986dcb6f8c56e896301cc49772f Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 30 Oct 2020 22:47:23 -0600 Subject: [PATCH 19/41] Added n?cry --- commands/cry.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 commands/cry.js diff --git a/commands/cry.js b/commands/cry.js new file mode 100644 index 0000000..d16a876 --- /dev/null +++ b/commands/cry.js @@ -0,0 +1,32 @@ +const Discord = require('discord.js'); +const Saves = require('../models/saves'); +const UserData = require('../models/user'); +const makeId = require('../util/makeid'); + +module.exports = { + name: "cry", + aliases: ['sob'], + help: "Tell others that you're crying with `{{p}}cry`. We're here for you!", + async execute(message, msg, args, cmd, prefix, mention, client) { + let savess = await Saves.findOne({name: 'cry'}) ? await Saves.findOne({name: 'cry'}) : new Saves({name: 'cry'}); + let saves = savess.saves; + if (!args.length) {return message.channel.send(new Discord.MessageEmbed() + .setTitle(`${message.guild ? message.member.displayName : message.author.username} is Crying!`) + .setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)])) + .setColor('8d42f5') + );} + if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) { + if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');} + let tu = await UserData.findOne({uid: message.author.id}); + if ((!tu || !tu.developer) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Developer in order to add new cry GIFs.");} + let e = true; + let id; + while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}} + args.shift(); + saves.set(id, args.join(" ").trim()); + savess.saves = saves; + savess.save(); + return message.channel.send("Save added!"); + } + } +}; \ No newline at end of file From a217960cbc9a943efb59f90c2050ae52d0c80f19 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 30 Oct 2020 22:47:34 -0600 Subject: [PATCH 20/41] Added n?hug --- commands/hug.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 commands/hug.js diff --git a/commands/hug.js b/commands/hug.js new file mode 100644 index 0000000..41dae4b --- /dev/null +++ b/commands/hug.js @@ -0,0 +1,46 @@ +const Discord = require('discord.js'); +const Saves = require('../models/saves'); +const UserData = require('../models/user'); +const makeId = require('../util/makeid'); + +module.exports = { + name: "hug", + help: "Tell others that you need a hug with `{{p}}hug`, or give one by mentioning someone to hug!", + async execute(message, msg, args, cmd, prefix, mention, client) { + let savess = await Saves.findOne({name: 'hug'}) ? await Saves.findOne({name: 'hug'}) : new Saves({name: 'hug'}); + let saves = savess.saves; + if (!args.length) { + return message.channel.send(message.guild ? new Discord.MessageEmbed() + .setTitle(`${message.guild ? message.member.displayName : message.author.username} needs a hug!`) + .setThumbnail(message.author.avatarURL({size: 2048})) + .setDescription(`Show them some love with \`${prefix}hug @${message.member.displayName}\`!`) + .setColor('c375f0') + .setFooter('Natsuki', client.user.avatarURL()) + .setTimestamp() + : "Sorry, but I'm a bot, and I can't hug you. Go into a server and ask for some hugs!" + );} + if (mention && args[0].match(/^<@(?:!?)(?:\d+)>$/)) { + if (!message.guild) {return message.reply("Please make sure you're in a server so you can mention someone other than me to hug!");} + if (!message.guild.members.cache.has(mention.id)) {return message.reply("That user is not in this server!");} + if (message.author.id === mention.id) {return message.reply("Sorry if you're that lonely, but you can't hug yourself!");} + return message.channel.send(new Discord.MessageEmbed() + .setAuthor(`${message.guild ? message.member.displayName : message.author.username} gives ${message.guild.members.cache.get(mention.id).displayName} a hug!`, message.author.avatarURL()) + .setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)])) + .setColor('52c7bb') + ); + } + if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) { + if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');} + let tu = await UserData.findOne({uid: message.author.id}); + if ((!tu || !tu.developer) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Developer in order to add new hug GIFs.");} + let e = true; + let id; + while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}} + args.shift(); + saves.set(id, args.join(" ").trim()); + savess.saves = saves; + savess.save(); + return message.channel.send("Save added!"); + } + } +}; \ No newline at end of file From fb4a418af35c3fc1f8e4c8dc9107361a11ea4ba3 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 31 Oct 2020 05:41:19 -0600 Subject: [PATCH 21/41] Completed n?kiss --- commands/kiss.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 commands/kiss.js diff --git a/commands/kiss.js b/commands/kiss.js new file mode 100644 index 0000000..97b3339 --- /dev/null +++ b/commands/kiss.js @@ -0,0 +1,46 @@ +const Discord = require('discord.js'); +const Saves = require('../models/saves'); +const UserData = require('../models/user'); +const makeId = require('../util/makeid'); + +module.exports = { + name: "kiss", + help: "Ask for a kiss with `{{p}}kiss`, or give one by mentioning someone!", + async execute(message, msg, args, cmd, prefix, mention, client) { + let savess = await Saves.findOne({name: 'kiss'}) ? await Saves.findOne({name: 'kiss'}) : new Saves({name: 'kiss'}); + let saves = savess.saves; + if (!args.length) { + return message.channel.send(message.guild ? new Discord.MessageEmbed() + .setTitle(`${message.guild ? message.member.displayName : message.author.username} wants a kiss!`) + .setThumbnail(message.author.avatarURL({size: 2048})) + .setDescription(`Give them a little kiss with \`${prefix}kiss @${message.member.displayName}\`!`) + .setColor('c375f0') + .setFooter('Natsuki', client.user.avatarURL()) + .setTimestamp() + : "I'm not really into that kind of thing. Maybe try asking in a server?" + );} + if (mention && args[0].match(/^<@(?:!?)(?:\d+)>$/)) { + if (!message.guild) {return message.reply("Please make sure you're in a server so you can mention someone other than me to kiss!");} + if (!message.guild.members.cache.has(mention.id)) {return message.reply("That user is not in this server!");} + if (message.author.id === mention.id) {return message.reply("A self-kiss ought to be a little hard, don't you think?");} + return message.channel.send(new Discord.MessageEmbed() + .setAuthor(`${message.guild ? message.member.displayName : message.author.username} kisses ${message.guild.members.cache.get(mention.id).displayName}`, message.author.avatarURL()) + .setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)])) + .setColor('d428a0') + ); + } + if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) { + if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');} + let tu = await UserData.findOne({uid: message.author.id}); + if ((!tu || !tu.developer) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Developer in order to add new kiss GIFs.");} + let e = true; + let id; + while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}} + args.shift(); + saves.set(id, args.join(" ").trim()); + savess.saves = saves; + savess.save(); + return message.channel.send("Save added!"); + } + } +}; \ No newline at end of file From f5ebc22b13c311010376ea19bbe2ed1f5adfd132 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 31 Oct 2020 05:41:31 -0600 Subject: [PATCH 22/41] Completed n?sip --- commands/sip.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 commands/sip.js diff --git a/commands/sip.js b/commands/sip.js new file mode 100644 index 0000000..e5dd740 --- /dev/null +++ b/commands/sip.js @@ -0,0 +1,31 @@ +const Discord = require('discord.js'); +const Saves = require('../models/saves'); +const UserData = require('../models/user'); +const makeId = require('../util/makeid'); + +module.exports = { + name: "sip", + help: "Take a sip and watch the shenanigans unfold using `{{p}}sip`.", + async execute(message, msg, args, cmd, prefix, mention, client) { + let savess = await Saves.findOne({name: 'sip'}) ? await Saves.findOne({name: 'sip'}) : new Saves({name: 'sip'}); + let saves = savess.saves; + if (!args.length) {return message.channel.send(new Discord.MessageEmbed() + .setTitle(`${message.guild ? message.member.displayName : message.author.username} takes a sip...`) + .setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)])) + .setColor('69310d') + );} + if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) { + if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');} + let tu = await UserData.findOne({uid: message.author.id}); + if ((!tu || !tu.developer) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Developer in order to add new sip GIFs.");} + let e = true; + let id; + while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}} + args.shift(); + saves.set(id, args.join(" ").trim()); + savess.saves = saves; + savess.save(); + return message.channel.send("Save added!"); + } + } +}; \ No newline at end of file From 1c0200e92f5526e058dacb45db5d576f6b5f8301 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 31 Oct 2020 05:41:42 -0600 Subject: [PATCH 23/41] Completed n?slap --- commands/slap.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 commands/slap.js diff --git a/commands/slap.js b/commands/slap.js new file mode 100644 index 0000000..a94a622 --- /dev/null +++ b/commands/slap.js @@ -0,0 +1,38 @@ +const Discord = require('discord.js'); +const Saves = require('../models/saves'); +const UserData = require('../models/user'); +const makeId = require('../util/makeid'); + +module.exports = { + name: "slap", + help: "Use `{{p}}slap @person` to have me personally deliver your anger to them with a nice s l a p.", + async execute(message, msg, args, cmd, prefix, mention, client) { + let savess = await Saves.findOne({name: 'slap'}) ? await Saves.findOne({name: 'slap'}) : new Saves({name: 'slap'}); + let saves = savess.saves; + if (!args.length) { + return message.channel.send(message.guild ? "Please mention someone to slap!" : "Oi! You don't get to waltz into my DM just to slap me!");} + if (mention && args[0].match(/^<@(?:!?)(?:\d+)>$/)) { + if (!message.guild) {return message.reply("Please make sure you're in a server so you can mention someone other than me to slap!");} + if (!message.guild.members.cache.has(mention.id)) {return message.reply("That user is not in this server!");} + if (message.author.id === mention.id) {return message.reply("Wait wouldn't slapping yourself be a form of self-harm? ToS is that you??");} + return message.channel.send(new Discord.MessageEmbed() + .setAuthor(`${message.guild ? message.member.displayName : message.author.username} slaps ${message.guild.members.cache.get(mention.id).displayName}`, message.author.avatarURL()) + .setImage(String(Array.from(saves.values())[Math.floor(Math.random() * saves.size)])) + .setColor('d93846') + ); + } + if (['s', 'save', 'n', 'new', 'a', 'add'].includes(args[0].toLowerCase())) { + if (!args[1]) {return message.channel.send('oi there cunt, give me a link of an image to add!');} + let tu = await UserData.findOne({uid: message.author.id}); + if ((!tu || !tu.developer) && !client.misc.savers.includes(message.author.id)) {return message.reply("You must be a Natsuki Developer in order to add new slap GIFs.");} + let e = true; + let id; + while (e === true) {id = makeId(6); if (!saves.has(id)) {e = false;}} + args.shift(); + saves.set(id, args.join(" ").trim()); + savess.saves = saves; + savess.save(); + return message.channel.send("Save added!"); + } + } +}; \ No newline at end of file From 8b1543304ad7dbe85d837b5b7adb166626651f6c Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 31 Oct 2020 06:55:16 -0600 Subject: [PATCH 24/41] add n?leave and member leave functionality --- commands/leave.js | 49 +++++++++++++++++++++++++++++++++++++ events/guildMemberRemove.js | 15 ++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 commands/leave.js create mode 100644 events/guildMemberRemove.js diff --git a/commands/leave.js b/commands/leave.js new file mode 100644 index 0000000..a40c0fb --- /dev/null +++ b/commands/leave.js @@ -0,0 +1,49 @@ +const Discord = require('discord.js'); +const GuildData = require('../models/guild'); +const Responses = require('../models/responses'); +const sendResponse = require('../util/response/sendresponse'); + +module.exports = { + name: "leave", + aliases: ['lv', 'leavemsg', 'leavemessage', 'leavechannel', 'lch', 'lmsg', 'leavech'], + help: new Discord.MessageEmbed() + .setTitle("Help -> Leave Messages") + .setDescription("Set the channel and message for your leave messages!") + .addField("Syntax", "`leave `") + .addField("Notice", "You must be a staff or admin in your server to edit these settings.") + .addField("Responses", "Your leave message should be generated through a response using my `response` command, and then bound to the leave message by providing your response's name."), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!message.guild) {return message.reply("This command is server-only.");} + let tg = await GuildData.findOne({gid: message.guild.id}) ? await GuildData.findOne({gid: message.guild.id}) : new GuildData({gid: message.guild.id}); + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}welcome \``);} + if (['v', 'view', 'c', 'check'].includes(args[0].toLowerCase())) {} + if ((!tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole)) && !message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You can't do that without staff or admin permissions, silly!");} + + if (['s', 'set'].includes(args[0].toLowerCase())) { + if (!args[1]) {return message.reply("You need to specify a channel for your leave messages to be sent in!");} + let ch = message.mentions.channels.first() && args[1].match(/^<#(?:\d+)>$/) ? message.mentions.channels.first().id : message.guild.channels.cache.has(args[1]) ? message.guild.channels.cache.get(args[1]).id : null; + if (!ch) {return message.reply("I can't find that channel!");} + if (!message.guild.channels.cache.get(ch).permissionsFor(client.user.id).has("SEND_MESSAGES")) {return message.reply("I can't send messages in that channel. Try fixing the permissions or using a different channel!");} + if (!args[2]) {return message.reply(`You have to specify a response to use! You can make one with \`${prefix}response new\`.`);} + let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id}); + if (!tr.responses.has(args[2].toLowerCase())) {return message.reply("Silly, I can't let you know that someone left with a response that doesn't exist! Try making one or make sure you spelled the name correctly.");} + tg.lch = ch; + tg.save(); + tr.bindings.set('leave', args[2].toLowerCase()); + tr.save(); + return message.channel.send(new Discord.MessageEmbed() + .setTitle("Leave Channel/Message Updated") + .setDescription(`This server's leave-notifying settings have been altered by ${message.author.tag}.\n\n**Channel**: <#${ch}>\n**Response Name**: \`${args[2].toLowerCase()}\``) + .setColor('c375f0') + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp() + ) + } + + if (['t', 'test'].includes(args[0].toLowerCase())) { + let tr = await Responses.findOne({gid: message.guild.id}); + if (!tr || !tr.bindings.has('leave') || !tr.responses.has(tr.bindings.get('leave'))) {return message.reply("I can't test your leave message because the response doesn't exist, a leave response isn't set, or you haven't made any responses in this server.");} + await sendResponse(message.member, message.channel, 'this shit aint matter anymore lol', client, tr.responses.get(tr.bindings.get('leave'))); + } + } +}; \ No newline at end of file diff --git a/events/guildMemberRemove.js b/events/guildMemberRemove.js new file mode 100644 index 0000000..45691b3 --- /dev/null +++ b/events/guildMemberRemove.js @@ -0,0 +1,15 @@ +const GuildData = require('../models/guild'); +const Responses = require('../models/responses'); +const sendResponse = require('../util/response/sendresponse'); + +module.exports = async (client, member) => { + let tg = await GuildData.findOne({gid: member.guild.id}); + let tr = await Responses.findOne({gid: member.guild.id}); + if ( + tr && tr.bindings.has('leave') && tr.responses.has(tr.bindings.get('leave')) + && tg.lch.length && member.guild.channels.cache.has(tg.lch) + && member.guild.channels.cache.get(tg.lch).permissionsFor(client.user.id).has("SEND_MESSAGES") + ) { + member.guild.channels.cache.get(tg.lch).send(await sendResponse(member, member.guild.channels.cache.get(tg.lch), 'xdlol', client, tr.responses.get(tr.bindings.get('leave')))); + } +}; \ No newline at end of file From 92044152bf05b5f32ab3da41b9a1e1721f2cd759 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sat, 14 Nov 2020 20:05:12 -0700 Subject: [PATCH 25/41] cant be bothered to enter a commit message --- bot.js | 1 + commands/leave.js | 17 +++++++++++++++++ commands/response.js | 5 +++++ commands/secretsanta.js | 34 ++++++++++++++++++++++++++++++++++ commands/staffrole.js | 2 +- commands/welcome.js | 17 +++++++++++++++++ events/guildMemberAdd.js | 3 ++- events/guildMemberRemove.js | 3 ++- util/response/sendresponse.js | 6 ++++-- 9 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 commands/secretsanta.js diff --git a/bot.js b/bot.js index cc3127f..5919018 100644 --- a/bot.js +++ b/bot.js @@ -15,6 +15,7 @@ async function init() { client.developers = ["330547934951112705", "673477059904929802"]; client.misc = {}; client.misc.savers = ['497598953206841375']; + client.misc.activeDMs = new Discord.Collection(); client.utils = {}; client.utils.logch = async () => {return client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915');}; diff --git a/commands/leave.js b/commands/leave.js index a40c0fb..c5a919e 100644 --- a/commands/leave.js +++ b/commands/leave.js @@ -45,5 +45,22 @@ module.exports = { if (!tr || !tr.bindings.has('leave') || !tr.responses.has(tr.bindings.get('leave'))) {return message.reply("I can't test your leave message because the response doesn't exist, a leave response isn't set, or you haven't made any responses in this server.");} await sendResponse(message.member, message.channel, 'this shit aint matter anymore lol', client, tr.responses.get(tr.bindings.get('leave'))); } + + if (['clear'].includes(args[0].toLowerCase())) { + tg.lch = ''; + tg.save(); + let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id}); + if (tr) { + tr.bindings.delete('leave'); + tr.save(); + } + return message.channel.send(new Discord.MessageEmbed() + .setTitle("Leave Channel/Message Updated") + .setDescription(`This server's leave-notifying settings have been altered by ${message.author.tag}.\n\n**Channel**: None`) + .setColor('c375f0') + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp() + ); + } } }; \ No newline at end of file diff --git a/commands/response.js b/commands/response.js index cf3d5bb..e285dd9 100644 --- a/commands/response.js +++ b/commands/response.js @@ -66,6 +66,11 @@ module.exports = { if (hasBinding) {infoEmbed.addField("Server Bindings", bm);} return message.channel.send(infoEmbed); } + if (['edit', 'e', 'm', 'modify'].includes(args[0].toLowerCase())) { + let options = await getResponse(message, args[1]); + if (!options) {return;} + + } return message.channel.send(`Syntax: \`${prefix}response \``); diff --git a/commands/secretsanta.js b/commands/secretsanta.js new file mode 100644 index 0000000..ae12512 --- /dev/null +++ b/commands/secretsanta.js @@ -0,0 +1,34 @@ +const Discord = require('discord.js'); + +const ask = require('../util/ask'); + +module.exports = { + name: "secretsanta", + aliases: ['ss'], + help: new Discord.MessageEmbed() + .setTitle("Help -> Secret Santa") + .setDescription("Create a secret santa for all of your friends or for your server! Whether you celebrate the holidays or not, this can still be loads of fun!") + .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 (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 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 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 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 (!join) {return;} + + } + } +}; \ No newline at end of file diff --git a/commands/staffrole.js b/commands/staffrole.js index 872c630..4b2e494 100644 --- a/commands/staffrole.js +++ b/commands/staffrole.js @@ -10,7 +10,7 @@ module.exports = { if (!message.guild) {return message.reply("This is a guild-only command!");} if (!args.length) {return message.channel.send(`Syntax: \`${prefix}staffrole <@role|roleID|clear|view>\``);} if (!message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You must be an admin in this server in order to change this setting!");} - + let tguild = await GuildSettings.findOne({gid: message.guild.id}) ? await GuildSettings.findOne({gid: message.guild.id}) : new GuildSettings({gid: message.guild.id}); diff --git a/commands/welcome.js b/commands/welcome.js index 40a3adb..3ddade7 100644 --- a/commands/welcome.js +++ b/commands/welcome.js @@ -45,5 +45,22 @@ module.exports = { if (!tr || !tr.bindings.has('welcome') || !tr.responses.has(tr.bindings.get('welcome'))) {return message.reply("I can't test your welcome message because the response doesn't exist, a welcome response isn't set, or you haven't made any responses in this server.");} await sendResponse(message.member, message.channel, 'this shit aint matter anymore lol', client, tr.responses.get(tr.bindings.get('welcome'))); } + + if (['clear'].includes(args[0].toLowerCase())) { + tg.wch = ''; + tg.save(); + let tr = await Responses.findOne({gid: message.guild.id}) ? await Responses.findOne({gid: message.guild.id}) : new Responses({gid: message.guild.id}); + if (tr) { + tr.bindings.delete('welcome'); + tr.save(); + } + return message.channel.send(new Discord.MessageEmbed() + .setTitle("Welcome Channel/Message Updated") + .setDescription(`This server's member-welcoming settings have been altered by ${message.author.tag}.\n\n**Channel**: None`) + .setColor('c375f0') + .setFooter("Natsuki", client.user.avatarURL()) + .setTimestamp() + ); + } } }; \ No newline at end of file diff --git a/events/guildMemberAdd.js b/events/guildMemberAdd.js index 5985e4f..f837d38 100644 --- a/events/guildMemberAdd.js +++ b/events/guildMemberAdd.js @@ -12,7 +12,8 @@ module.exports = async (client, member) => { tr && tr.bindings.has('welcome') && tr.responses.has(tr.bindings.get('welcome')) && tg.wch.length && member.guild.channels.cache.has(tg.wch) && member.guild.channels.cache.get(tg.wch).permissionsFor(client.user.id).has("SEND_MESSAGES") + && !client.users.cache.get(member.id).bot ) { - member.guild.channels.cache.get(tg.wch).send(await sendResponse(member, member.guild.channels.cache.get(tg.wch), 'xdlol', client, tr.responses.get(tr.bindings.get('welcome')))); + try {member.guild.channels.cache.get(tg.wch).send(await sendResponse(member, member.guild.channels.cache.get(tg.wch), 'xdlol', client, tr.responses.get(tr.bindings.get('welcome'))));} catch {} } }; \ No newline at end of file diff --git a/events/guildMemberRemove.js b/events/guildMemberRemove.js index 45691b3..63d8fcf 100644 --- a/events/guildMemberRemove.js +++ b/events/guildMemberRemove.js @@ -9,7 +9,8 @@ module.exports = async (client, member) => { tr && tr.bindings.has('leave') && tr.responses.has(tr.bindings.get('leave')) && tg.lch.length && member.guild.channels.cache.has(tg.lch) && member.guild.channels.cache.get(tg.lch).permissionsFor(client.user.id).has("SEND_MESSAGES") + && !client.users.cache.get(member.id).bot ) { - member.guild.channels.cache.get(tg.lch).send(await sendResponse(member, member.guild.channels.cache.get(tg.lch), 'xdlol', client, tr.responses.get(tr.bindings.get('leave')))); + try {member.guild.channels.cache.get(tg.lch).send(await sendResponse(member, member.guild.channels.cache.get(tg.lch), 'xdlol', client, tr.responses.get(tr.bindings.get('leave'))));} catch {} } }; \ No newline at end of file diff --git a/util/response/sendresponse.js b/util/response/sendresponse.js index 1b55938..74ec7e1 100644 --- a/util/response/sendresponse.js +++ b/util/response/sendresponse.js @@ -10,8 +10,10 @@ module.exports = async(member, channel, mode, client, options) => { var responseEmbed = new Discord.MessageEmbed().setTitle(options.title).setDescription(await filterResponse(member, client, options.description)); if (options.fieldnames && options.fieldnames.length) {let i; for (i=0;i Date: Sun, 15 Nov 2020 01:05:59 -0700 Subject: [PATCH 26/41] Client reloading; n?reload --- commands/reload.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++ handle/command.js | 3 ++- handle/event.js | 4 +++- 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 commands/reload.js diff --git a/commands/reload.js b/commands/reload.js new file mode 100644 index 0000000..80511e9 --- /dev/null +++ b/commands/reload.js @@ -0,0 +1,49 @@ +const Discord = require('discord.js'); +const fs = require('fs'); +const chalk = require('chalk'); + +module.exports = { + name: "reload", + aliases: ['relog', 'rel', 'refresh'], + help: new Discord.MessageEmbed() + .setTitle("Help -> System Reloading") + .setDescription("Reloads the system extensions by refreshing all command and event files into client without terminating the node process. *Hi I'm Wubzy and this makes no sense to anyone but discord.js devs because we're nerds*") + .addField("Syntax", "`refresh [log]`. Adding 'log' will log to the console as though the bot were in startup.") + .addField("Notice", "This command is only available to Natsuki developers."), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!args.length) { + var commands = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); + console.log(`\n${chalk.yellow('[WARN]')} >> ${chalk.gray('Reload:')} ${chalk.white('All commands and events are being reloaded!')}`); + console.log(`${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`Developer ${message.author.username} initiated the system refresh`)}\n`); + + ['commands', 'aliases'].forEach(x => client[x] = new Discord.Collection()); + for (let commandf of commands) { + if (Object.keys(require.cache).includes(require.resolve(`./${commandf}`))) {delete require.cache[require.resolve(`./${commandf}`)];} + var command = require(`./${commandf}`); + client.commands.set(command.name, command); + if (command.aliases) {command.aliases.forEach(a => client.aliases.set(a, command.name));} + } + console.log(`${chalk.gray('[LOG]')} >> ${chalk.blue('Loaded all Commands')}`); + + let eventFilter = fs.readdirSync('./events/').filter(x => x.endsWith('.js')); + for (let file of eventFilter) { + let evtName = file.split('.')[0]; + if (Object.keys(require.cache).includes(require.resolve('../events/' + file))) {delete require.cache[require.resolve('../events/' + file)];} + let evt = require('../events/' + file); + client.removeAllListeners(evtName); + client.on(evtName, evt.bind(null, client)); + } + console.log(`${chalk.gray('[LOG]')} >> ${chalk.blue('Loaded all Events')}`); + + console.log(`\n${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`Client refresh successful`)}\n`); + + return message.channel.send("Done!") + } + if (['l', 'log', 'ns', 'nosilent', 'notsilent'].includes(args[0].toLowerCase())) { + ['commands', 'aliases'].forEach(x => client[x] = new Discord.Collection()); + ['command', 'event'].forEach(x => require(`../handle/${x}`)(client)); + return message.channel.send("Done!"); + } + else {return message.channel.send("Oi! 'log' is the only valid arg to use. Use no args if you want a cleaner console output instead.");} + } +}; \ No newline at end of file diff --git a/handle/command.js b/handle/command.js index 231d101..2f88f53 100644 --- a/handle/command.js +++ b/handle/command.js @@ -6,10 +6,11 @@ module.exports = client => { var commands = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); console.log(`\n${chalk.gray('[BOOT]')} >> ${chalk.blue('Getting Commands...')}\n`); for (let commandf of commands) { + if (Object.keys(require.cache).includes(require.resolve(`../commands/${commandf}`))) {delete require.cache[require.resolve(`../commands/${commandf}`)];} var command = require(`../commands/${commandf}`); client.commands.set(command.name, command); if (command.aliases) {command.aliases.forEach(a => client.aliases.set(a, command.name));} - console.log(`${chalk.gray('[LOG] ')} >> ${chalk.blueBright('Loaded Command')} ${chalk.white(command.name)}`); + console.log(`${chalk.gray('[LOG] ')} >> ${chalk.blueBright('Loaded Command')} ${chalk.white(command.name)} ${chalk.blueBright('with')} ${chalk.white(command.aliases && command.aliases.length ? command.aliases.length : 0)} ${chalk.blueBright('aliases')}`); } console.log(`\n${chalk.gray('[BOOT]')} >> ${chalk.blue('Loaded all Commands')}`); }; \ No newline at end of file diff --git a/handle/event.js b/handle/event.js index 443357b..ead4b3c 100644 --- a/handle/event.js +++ b/handle/event.js @@ -6,8 +6,10 @@ module.exports = client => { let eventFilter = fs.readdirSync('./events/').filter(x => x.endsWith('.js')); console.log(`\n${chalk.gray('[BOOT]')} >> ${chalk.blue('Getting Events...')}\n`); for (let file of eventFilter) { - let evt = require('../events/' + file); let evtName = file.split('.')[0]; + if (Object.keys(require.cache).includes(require.resolve('../events/' + file))) {delete require.cache[require.resolve('../events/' + file)];} + let evt = require('../events/' + file); + client.removeAllListeners(evtName); client.on(evtName, evt.bind(null, client)); console.log(`${chalk.gray('[LOG] ')} >> ${chalk.blueBright('Loaded Event')} ${chalk.white(evtName)}`); } From 930b9dddd6a1ec819875fc124de186a5c39d9b24 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sun, 15 Nov 2020 15:33:43 -0700 Subject: [PATCH 27/41] cba for commit messages --- commands/secretsanta.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/commands/secretsanta.js b/commands/secretsanta.js index ae12512..407efd6 100644 --- a/commands/secretsanta.js +++ b/commands/secretsanta.js @@ -27,8 +27,15 @@ module.exports = { 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 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 (!join) {return;} - + 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 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;} + 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!");} + + } } }; \ No newline at end of file From 017c11c930b56342401da0e480348f150b86b72b Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Sun, 15 Nov 2020 16:45:17 -0700 Subject: [PATCH 28/41] Fix n?deathnote --- commands/deathnote.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/commands/deathnote.js b/commands/deathnote.js index 6e23da1..f2a51e0 100644 --- a/commands/deathnote.js +++ b/commands/deathnote.js @@ -59,21 +59,24 @@ module.exports = { async execute(message, msg, args, cmd, prefix, mention, client) { if (!message.guild) {return message.reply("Unfortunately, this is a **guild-only** command!");} if (!args.length) {return message.channel.send(`Syntax: \`${prefix}deathnote <@member> [method of death]\``);} - if (args[0] == "kill" || args[0] == "k") {args.shift();} // if someone adds in 'kill' it'll remove it and act like it wasn't there, proceeding as normal. + if (args[0] === "kill" || args[0] === "k") {args.shift();} // if someone adds in 'kill' it'll remove it and act like it wasn't there, proceeding as normal. //if (!args[0].trim().match(/^<@(?:\!?)\d+>$/)) {return message.reply("You have to mention someone!");} - if (mention && mention.id == message.author.id) {return message.reply("Hehe I won't let you write your own name in the notebook! Just leave it somewhere for a few days and someone else will take it. Maybe they'll write your name...");} // users can't mention themselves - if (mention && mention.id == client.user.id) {return message.reply("You can't kill me! Little did you know, I'm actually a death god!");} + if (mention && mention.id === message.author.id) {return message.reply("Hehe I won't let you write your own name in the notebook! Just leave it somewhere for a few days and someone else will take it. Maybe they'll write your name...");} // users can't mention themselves + if (mention && mention.id === client.user.id) {return message.reply("You can't kill me! Little did you know, I'm actually a death god!");} //TODO if bot is mentioned maybe - let death = deaths[Math.floor(Math.random() * deaths.length)]; //kill method let reptype = responses[Object.keys(responses)[Math.floor(Math.random() * Object.keys(responses).length)]]; // report type let title = reptype.titles[Math.floor(Math.random() * reptype.titles.length)]; - let options = new TagFilter([ new Tag(['method', '-m', 'cause', '-c'], 'method', 'append'), new Tag(['victim', 'v', 'against', 'a', 'name', 'n'], 'victim', 'append') ]).test(args.join(" ")); + + let death = (!options.victim || (options.victim && !options.victim.length)) && (!options.method || (options.method && !options.method.length)) && args.length > 1 + ? args.join(" ").slice(args[0].length + 1) + : deaths[Math.floor(Math.random() * deaths.length)]; //kill method if (options.method && options.method.length) {death = options.method;} + if (death.length > 750) {return message.channel.send("I'd rather you didn't try to fill the death note with a 7-page double-spaced essay in Times New Roman containing an advanced trajectory theorem on the death of your poor target.");} if (!mention && (!options.victim || !options.victim.length)) {return message.reply("You have to write their name down in order to kill them! (In other words, please mention the user whose name you wish to write.)");} @@ -81,7 +84,7 @@ module.exports = { let vargs = options.victim.trim().split(/\s+/g); let nvargs = []; let varg; for (varg of vargs) { - if (varg.match(/^<@(?:\!?)\d+>$/)) { + if (varg.match(/^<@(?:!?)\d+>$/)) { nvargs.push(message.guild.members.cache.has(varg.slice(varg.search(/\d/), varg.search('>'))) ? message.guild.members.cache.get(varg.slice(varg.search(/\d/), varg.search('>'))).displayName : varg); } else {nvargs.push(varg);} } From 62c861e9d5ae34b2f549645f96321d9f58466a86 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Tue, 17 Nov 2020 17:08:06 -0700 Subject: [PATCH 29/41] Fix n?eval error outputting --- commands/eval.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/commands/eval.js b/commands/eval.js index 75ae8bd..cfdfa58 100644 --- a/commands/eval.js +++ b/commands/eval.js @@ -1,4 +1,5 @@ const Discord = require('discord.js'); +const chalk = require('chalk'); module.exports = { name: 'eval', @@ -24,11 +25,11 @@ module.exports = { .setColor('c375f0') .setFooter(`Natsuki`, client.user.avatarURL()) .setTimestamp()) - }); + }).catch(error => {return message.channel.send(`Error: \`${error}\`.`);}); } catch (error) { - //let date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6); - //console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to run n?eval`)}`, error); + let date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6); + console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to run n?eval`)}`, error); return message.channel.send(`Error: \`${error}\`.`); - }; + } }, }; \ No newline at end of file From 88ee8450a52de66eff1e25b31bc2bffec686cf93 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Thu, 19 Nov 2020 20:19:18 -0700 Subject: [PATCH 30/41] 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 { From 0db25f4478deafd9f34a5f9867b478b17d400222 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Thu, 19 Nov 2020 20:41:59 -0700 Subject: [PATCH 31/41] add n?pull --- commands/pull.js | 29 +++++++++++++++++++++++++++++ commands/reload.js | 4 ++++ 2 files changed, 33 insertions(+) create mode 100644 commands/pull.js diff --git a/commands/pull.js b/commands/pull.js new file mode 100644 index 0000000..eac43a0 --- /dev/null +++ b/commands/pull.js @@ -0,0 +1,29 @@ +const Discord = require('discord.js'); +const fs = require('fs'); +const chalk = require('chalk'); +const UserData = require('../models/user'); +const cp = require('child_process'); + +module.exports = { + name: "pull", + help: new Discord.MessageEmbed() + .setTitle("Help -> VCS Pull") + .setDescription("Pulls new commits from VCS") + .addField("Syntax", "`refresh`") + .addField("Notice", "This command is only available to Natsuki developers."), + async execute(message, msg, args, cmd, prefix, mention, client) { + const tu = await UserData.findOne({uid: message.author.id}); + if (!tu || !tu.developer) {return message.channel.send("You must be a Natsuki developer in order to do this!");} + + console.log(`\n${chalk.yellow('[WARN]')} >> ${chalk.gray('VCS:')} ${chalk.white('Initiating remote->local VCS sync/refresh!')}`); + + cp.exec("git pull origin master", function(error, stdout, stderr) { + if (stderr || error) { + let date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6); + console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to pull from VCS`)}`, stderr || error); + } else { + console.log(`\n${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`VCS Pull successful`)}\n`); + } + }); + } +}; \ No newline at end of file diff --git a/commands/reload.js b/commands/reload.js index 80511e9..9793385 100644 --- a/commands/reload.js +++ b/commands/reload.js @@ -1,6 +1,7 @@ const Discord = require('discord.js'); const fs = require('fs'); const chalk = require('chalk'); +const UserData = require("../models/user"); module.exports = { name: "reload", @@ -12,6 +13,9 @@ module.exports = { .addField("Notice", "This command is only available to Natsuki developers."), async execute(message, msg, args, cmd, prefix, mention, client) { if (!args.length) { + const tu = await UserData.findOne({uid: message.author.id}); + if (!tu || !tu.developer) {return message.channel.send("You must be a Natsuki developer in order to do this!");} + var commands = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); console.log(`\n${chalk.yellow('[WARN]')} >> ${chalk.gray('Reload:')} ${chalk.white('All commands and events are being reloaded!')}`); console.log(`${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`Developer ${message.author.username} initiated the system refresh`)}\n`); From a8288aef1fdd2242a7142b3b1b64e88ccdee6380 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 20 Nov 2020 13:08:40 -0700 Subject: [PATCH 32/41] add starting to secret santa... probably --- commands/pull.js | 1 + commands/secretsanta.js | 39 +++++++++++++++++++++++---------------- template.js | 20 ++++++++++++++++++++ test.js | 15 +++++++++++---- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/commands/pull.js b/commands/pull.js index eac43a0..7daab80 100644 --- a/commands/pull.js +++ b/commands/pull.js @@ -24,6 +24,7 @@ module.exports = { } else { console.log(`\n${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`VCS Pull successful`)}\n`); } + return message.channel.send(`Done with ${stderr || error ? 'an error' : 'no errors'}!`); }); } }; \ No newline at end of file diff --git a/commands/secretsanta.js b/commands/secretsanta.js index 48ec162..40e40cd 100644 --- a/commands/secretsanta.js +++ b/commands/secretsanta.js @@ -145,25 +145,32 @@ module.exports = { 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 dm = []; let rm; + let m; for (m of tss.members) {dm.push({name: m.id, assignedTo: null});} + for (m of dm) { + while (true) { + let rm = tss.members[Math.floor(Math.random() * tss.members.length)]; + let exists = false; + let cdm; for (cdm of dm) {if (!exists) {exists = cdm.assignedTo === rm.id;}} + if (!exists && rm.id !== m.name) {dm[dm.indexOf(m)] = {name: m.name, assignedTo: rm.id}; break;} } } - let i; for (i=0;i { + let host = await client.users.fetch(tss.owner); + await host.send(`There was a problem sending ${mem.name} their info. Please tell that member that they have been assigned to \`${assignment.name}\` and that they want \`${assignment.info}\`.`); + }); } - let mg; let asg = []; for (mg of dm) {asg.push({name: mg[0].id, assignedTo: mg[1].id});} - tss.assignments = asg; + message.channel.send("**The secret santa has been started!** Everyone should have their assignments."); } } }; \ No newline at end of file diff --git a/template.js b/template.js index a16aa35..74475a2 100644 --- a/template.js +++ b/template.js @@ -3,6 +3,16 @@ const Discord = require('discord.js'); module.exports = { name: "", aliases: [], + meta: { + category: "", + perms: "", + staff: false, + vip: "", + serverPerms: [], + writtenBy: "", + serverOnly: false + }, + tags: [], help: new Discord.MessageEmbed() .setTitle("Help -> ") .setDescription("") @@ -19,6 +29,16 @@ const Discord = require('discord.js'); module.exports = { name: "", aliases: [], + meta: { + category: "", + perms: "", + staff: false, + vip: "", + serverPerms: [], + writtenBy: "", + serverOnly: false + }, + tags: [], help: "", async execute(message, msg, args, cmd, prefix, mention, client) { if (!args.length) {return message.channel.send(`Syntax: \`${prefix}\``);} diff --git a/test.js b/test.js index 94412ed..759f5ca 100644 --- a/test.js +++ b/test.js @@ -34,9 +34,16 @@ let tss = { assignments: [] } -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; +let dm = []; let rm; +let m; for (m of tss.members) {dm.push({name: m.id, assignedTo: null});} +for (m of dm) { + while (true) { + let rm = tss.members[Math.floor(Math.random() * tss.members.length)]; + let exists = false; + let cdm; for (cdm of dm) {if (!exists) {exists = cdm.assignedTo === rm.id;}} + if (!exists && rm.id !== m.name) {dm[dm.indexOf(m)] = {name: m.name, assignedTo: rm.id}; break;} + } +} +tss.assignments = dm; console.log(tss); \ No newline at end of file From 2079aa62d7e31609d567a4c0a51db64b1fb39497 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 20 Nov 2020 13:10:50 -0700 Subject: [PATCH 33/41] fix smol brain moment --- commands/secretsanta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/secretsanta.js b/commands/secretsanta.js index 40e40cd..aeb5147 100644 --- a/commands/secretsanta.js +++ b/commands/secretsanta.js @@ -12,7 +12,7 @@ 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', 'c', 'n', 's'].includes(args[0].toLowerCase())) { + if (['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'); From bc888b9c111d42c5bfdb599cdd44727847fd8bee Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 20 Nov 2020 21:42:12 +0000 Subject: [PATCH 34/41] some shit is done yes --- commands/blacklist.js | 88 +++++++++++++++++++++++++++++++++++++++++++ models/guild.js | 3 +- package-lock.json | 6 +-- 3 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 commands/blacklist.js diff --git a/commands/blacklist.js b/commands/blacklist.js new file mode 100644 index 0000000..265ed32 --- /dev/null +++ b/commands/blacklist.js @@ -0,0 +1,88 @@ +const Discord = require('discord.js'); +const UserData = require('../models/user'); +const GuildData = require('../models/guild') + +module.exports = { + name: "blacklist", + aliases: ['bl'], + help: "Disables a user from using Natsuki ( Usage: {{p}}blacklist )", + async execute(message, msg, args, cmd, prefix, mention, client) { + + let tu = await UserData.findOne({ uid: message.author.id }); + + if (['g', 'guild'].includes(args[0].toLowerCase())) { + if(!tu || !tu.admin) { return message.channel.send('Sorry... you have to be a Natsuki Admin to do this!');} + + let guild = !args[1].match(/\d+/) ? message.guild ? message.guild : null : client.guilds.cache.has(args[1]) ? client.guilds.cache.get(args[1]) : null; + if (!guild) {return message.channel.send("You must provide a guild ID or be in a guild that you wish to blacklist!");} + + let tg = await GuildData.findOne({gid: guild.id}) || new GuildData({gid: guild.id}); + + if (args[1].match(/\d+/)) {args.shift();} + if (!args[1]) {return message.channel.send("You must specify whether to `add` or `del` a guild's blacklist!");} + + let tu = await UserData.findOne({uid: message.author.id}); + if (!tu || !tu.admin) {return message.reply("You must be a Natsuki Admin to blacklist!");} + if (message.guild.id === "762707532417335296") {return message.reply("You can't blacklist my support server!");} + + if (['a', 'add'].includes(args[1].toLowerCase())) { + if (tg.blacklisted) {return message.reply("That guild is already blacklisted!");} + tg.blacklisted = true; + tg.save(); + return message.channel.send("Gotcha! This server will not be able to use my commands!"); + } + + if (['r', 'rem', 'remove', 'd', 'del', 'delete'].includes(args[1].toLowerCase())) { + if (tg.blacklisted) {return message.reply("That guild isn't blacklisted in the first place!");} + tg.blacklisted = false; + tg.save(); + return message.channel.send("I have graced your merciful request; this server can once again make use of my wonderous abilities!"); + } + + return message.channel.send("Valid args: `[guildID] `"); + + } + + if (['u', 'user'].includes(args[0].toLowerCase())) { + + args.shift(); + + if (!args[1]) {return message.channel.send("You must specify whether to `add` or `del` a user's blacklist!");} + + function checkPerms(tu, bu) { + if (!tu.developer && bu.support) {message.channel.send("You can't blacklist any member of staff unless you're a developer!"); return null;} + if (!tu.admin) {message.channel.send("You must be at least admin to do that!"); return null;} + if (bu.developer) {message.channel.send("Developers cannot be blacklisted!"); return null;} + } + + if(['a', 'add'].includes(args[1].toLowerCase())) { + let blacklistUser = args[0].match(/^<@(?:!?)(?:\d+)>$/) && mention && client.users.cache.has(mention.id) ? mention.id : client.users.cache.has(args[0]) ? client.users.cache.get(args[0]).id : null; + if (!blacklistUser) {return message.reply("You must specify a user to blacklist!");} + let usersData = await UserData.findOne( { uid: blacklistUser } ) || new UserData({uid: blacklistUser}); + + if (!checkPerms(tu, usersData);) {return;} + + if (usersData.blacklisted === true) {return message.reply('they\'re already blacklisted :eyes:');} + + await UserData.findOneAndUpdate({ uid: blacklistUser }, { blacklisted: true }.catch(() => {})); + return message.channel.send(`Another one bites the dust! **${blacklistUser.user.tag}** has been blacklisted!`) + } + + if(['r', 'rem', 'remove', 'd', 'del', 'delete'].includes(args[1].toLowerCase())) { + let blacklistedUser = args[0].match(/^<@(?:!?)(?:\d+)>$/) && mention && client.users.cache.has(mention.id) ? mention.id : client.users.cache.has(args[0]) ? client.users.cache.get(args[0]).id : null; + if (!blacklistedUser) { return message.reply("You need to specify who you're letting free..." );} + let userData = await UserData.findOne( { uid: blacklistedUser } ) || new UserData({uid: blacklistedUser}); + + if (!checkPerms(tu, userData);) {return;} + + if(userData.blacklisted === false) {return message.reply('hate to break it you... they\'re not even blacklisted!');} + + await UserData.findOneAndUpdate({ uid: blacklistedUser }, { blacklisted: false }.catch(() => {})); + return message.channel.send(`Alright, there you go, I unblacklisted **${blacklistedUser.user.tag}**`) + } + + return message.channel.send("Valid args: ` `"); + + } + + }}; \ No newline at end of file diff --git a/models/guild.js b/models/guild.js index a2ca5fd..480bd5e 100644 --- a/models/guild.js +++ b/models/guild.js @@ -18,7 +18,8 @@ const guildSchema = new mongoose.Schema({ nostatus: {type: Boolean, default: false}, starchannel: {type: String, default: ''}, starreq: {type: Number, default: 5}, - starsenabled: {type: Boolean, default: false} + starsenabled: {type: Boolean, default: false}, + blacklisted: {type: Boolean, default: false} }); module.exports = mongoose.model("guild", guildSchema); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 93413ab..8566a6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1428,9 +1428,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { "ms": "2.1.2" } From a459a39a2e06684f3697d81cb8c1b4862958ad14 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Fri, 27 Nov 2020 23:51:40 -0700 Subject: [PATCH 35/41] Status caching and ping warning ratelimit --- bot.js | 10 +++++++--- commands/afk.js | 4 ++++ commands/blacklist.js | 4 ++-- commands/dnd.js | 4 ++++ events/message.js | 9 ++++++++- events/ready.js | 27 ++++++++++++++++++++------- models/statuses.js | 8 ++++++++ package-lock.json | 21 --------------------- package.json | 1 - util/cachestatus.js | 11 +++++++++++ util/mention.js | 11 +++++++++-- util/siftstatuses.js | 25 +++++++++++++++++++++++++ 12 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 models/statuses.js create mode 100644 util/cachestatus.js create mode 100644 util/siftstatuses.js diff --git a/bot.js b/bot.js index 5919018..65e3280 100644 --- a/bot.js +++ b/bot.js @@ -13,9 +13,13 @@ async function init() { ['command', 'event'].forEach(x => require(`./handle/${x}`)(client)); client.developers = ["330547934951112705", "673477059904929802"]; - client.misc = {}; - client.misc.savers = ['497598953206841375']; - client.misc.activeDMs = new Discord.Collection(); + + client.misc = { + savers: ['497598953206841375'], + activeDMs: new Discord.Collection(), + statusPings: new Discord.Collection() + }; + client.utils = {}; client.utils.logch = async () => {return client.guilds.cache.get('762707532417335296').channels.cache.get('762732961753595915');}; diff --git a/commands/afk.js b/commands/afk.js index ec46965..d73f251 100644 --- a/commands/afk.js +++ b/commands/afk.js @@ -24,7 +24,11 @@ module.exports = { if (reason.length > 150) {return message.reply("That status a bit long; keep it under 150 characters.");} tu.statustype = 'afk'; tu.statusmsg = reason.trim(); + tu.statussetat = new Date(); + let tempDate = new Date(); + tu.statusclearat = tempDate.setHours(tempDate.getHours() + 12); tu.save(); + require('../util/cachestatus')(message.author.id, tempDate.setHours(tempDate.getHours() + 12)); return message.reply(`I set your ${tu.statusclearmode === 'auto' ? 'automatically' : 'manually'}-clearing AFK message to: ${reason.trim()}`); } }; \ No newline at end of file diff --git a/commands/blacklist.js b/commands/blacklist.js index 265ed32..e6e59cd 100644 --- a/commands/blacklist.js +++ b/commands/blacklist.js @@ -60,7 +60,7 @@ module.exports = { if (!blacklistUser) {return message.reply("You must specify a user to blacklist!");} let usersData = await UserData.findOne( { uid: blacklistUser } ) || new UserData({uid: blacklistUser}); - if (!checkPerms(tu, usersData);) {return;} + if (!checkPerms(tu, usersData)) {return;} if (usersData.blacklisted === true) {return message.reply('they\'re already blacklisted :eyes:');} @@ -73,7 +73,7 @@ module.exports = { if (!blacklistedUser) { return message.reply("You need to specify who you're letting free..." );} let userData = await UserData.findOne( { uid: blacklistedUser } ) || new UserData({uid: blacklistedUser}); - if (!checkPerms(tu, userData);) {return;} + if (!checkPerms(tu, userData)) {return;} if(userData.blacklisted === false) {return message.reply('hate to break it you... they\'re not even blacklisted!');} diff --git a/commands/dnd.js b/commands/dnd.js index fee8705..b19057f 100644 --- a/commands/dnd.js +++ b/commands/dnd.js @@ -24,7 +24,11 @@ module.exports = { if (reason.length > 150) {return message.reply("That status a bit long; keep it under 150 characters.");} tu.statustype = 'dnd'; tu.statusmsg = reason.trim(); + tu.statussetat = new Date(); + let tempDate = new Date(); + tu.statusclearat = tempDate.setHours(tempDate.getHours() + 12); tu.save(); + require('../util/cachestatus')(message.author.id, tempDate.setHours(tempDate.getHours() + 12)); return message.reply(`I set your ${tu.statusclearmode === 'auto' ? 'automatically' : 'manually'}-clearing Do not Disturb message to: ${reason.trim()}`); } }; \ No newline at end of file diff --git a/events/message.js b/events/message.js index e3b5939..b58e5ad 100644 --- a/events/message.js +++ b/events/message.js @@ -1,8 +1,10 @@ const Discord = require('discord.js'); -const mongoose = require('mongoose'); const chalk = require('chalk'); + const wait = require('../util/wait'); + const UserData = require('../models/user'); +const StatusCache = require('../models/statuses'); module.exports = async (client, message) => { if (message.author.bot) {return undefined;} @@ -36,6 +38,11 @@ module.exports = async (client, message) => { tu.statusmsg = ''; tu.statustype = ''; tu.save(); + const statuses = await StatusCache.findOne({f: 'lol'}); + let status; for (status of statuses.statuses) { + if (status.id === message.author.id) {delete statuses.statuses.indexOf(status);} + } + statuses.save(); message.reply('Hey there! You asked me to clear your status when you send a message next, so I went ahead and did that for you.'); } diff --git a/events/ready.js b/events/ready.js index d76675e..c0a7818 100644 --- a/events/ready.js +++ b/events/ready.js @@ -2,7 +2,11 @@ const Discord = require('discord.js'); const chalk = require('chalk'); const moment = require('moment'); const mongoose = require('mongoose'); + const GuildSettings = require('../models/guild'); +const BotDataSchema = require('../models/bot'); + +const siftStatuses = require('../util/siftstatuses'); var prefix = 'n?'; @@ -11,7 +15,7 @@ module.exports = async client => { try { await mongoose.connect(`mongodb+srv://${config.database.user}:${config.database.password}@${config.database.cluster}.3jpp4.mongodb.net/test`, { useFindAndModify: false, useNewUrlParser: true, dbName: 'Natsuki-Main', useUnifiedTopology: true, useCreateIndex: true - }); + }); } catch (e) { let date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6); console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | Occurred while trying to connect to Mongo Cluster`)}`, e); @@ -30,27 +34,36 @@ module.exports = async client => { let responses = { "PLAYING": [ - `in ${client.guilds.cache.size} servers` + `with my darling`, 'RAIN: Shadow Lords' + ,`in ${client.guilds.cache.size} servers` ], "WATCHING": [ - `for ${client.commands.size} commands` + `for ${client.commands.size} commands`, + "I'm not a bad slime, slurp!", "Lelouch rule the world!", + "a slime somehow start an empire", "a fox-maid get her tail fluffed", + "a raccoon-girl and some guy with a shield", "some chick with unusually red hair", + "Mob hit 100", "a really bad harem anime", "The Black Swordsman", + "The Misfit of Demon King Academy" ,`over ${client.guilds.cache.size} servers` ] }; const setR = () => { let type = Object.keys(responses)[Math.floor(Math.random() * Object.keys(responses).length)]; - client.user.setActivity(responses[type][Math.floor(Math.random() * responses[type].length)] + " | " + prefix + "help", {type: type});}; + if (type === "PLAYING") {client.user.setActivity(responses[type][Math.floor(Math.random() * responses[type].length)] + " | " + prefix + "help");} + else {client.user.setActivity(responses[type][Math.floor(Math.random() * responses[type].length)] + " | " + prefix + "help", {type: type});} + } setR(); + setInterval(setR, 14400000); - - const BotDataSchema = require('../models/bot'); const setP = async () => {let tg; for (tg of Array.from(client.guilds.cache.values)) { let tguild = await GuildSettings.findOne({gid: tg.id}); if (tguild && tguild.prefix && tguild.prefix.length) {client.guildconfig.prefixes.set(tg.id, tguild.prefix);} }}; setP(); - setInterval(setP, 120000); + siftStatuses(); + + setInterval(() => {setP(); siftStatuses(client, null);}, 120000); let botData = await BotDataSchema.findOne({finder: 'lel'}) ? await BotDataSchema.findOne({finder: 'lel'}) diff --git a/models/statuses.js b/models/statuses.js new file mode 100644 index 0000000..e070c1a --- /dev/null +++ b/models/statuses.js @@ -0,0 +1,8 @@ +const mongoose = require('mongoose'); + +const StatusSchema = new mongoose.Schema({ + f: String, + statuses: [{id: String, clear: Date}] +}); + +module.exports = new mongoose.model('statuses', StatusSchema); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8566a6a..93dd4f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3789,11 +3789,6 @@ "ip-address": "^5.8.8" } }, - "@lavaclient/types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lavaclient/types/-/types-1.0.2.tgz", - "integrity": "sha512-7OmlW8PD0mU4n4qD79YiT86mJDMmV0qoPLdXnepqv067gf9204p5tXRrs/cr+vGljqewELyUtUuQd74rRTZViw==" - }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -7315,22 +7310,6 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "lavaclient": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lavaclient/-/lavaclient-3.1.2.tgz", - "integrity": "sha512-kfY6/3zkWyv3fc+G/Br8aEWrRBxwhgQy8xA3cJmIAxRGey8TC4kCM0gXKbAQDqcEErSAakRln18f+uwUkOExPA==", - "requires": { - "@lavaclient/types": "1.0.2", - "ws": "^7.3.1" - }, - "dependencies": { - "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" - } - } - }, "lavalink": { "version": "2.10.2", "resolved": "https://registry.npmjs.org/lavalink/-/lavalink-2.10.2.tgz", diff --git a/package.json b/package.json index d11bfef..de3ec0a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "ffmpeg-static": "^4.2.7", "gblapi.js": "^2.0.5", "heroku": "^7.42.1", - "lavaclient": "^3.1.2", "lavaqueue": "^3.1.6", "manyitems": "^1.0.2", "moment": "^2.28.0", diff --git a/util/cachestatus.js b/util/cachestatus.js new file mode 100644 index 0000000..da88db0 --- /dev/null +++ b/util/cachestatus.js @@ -0,0 +1,11 @@ +const StatusCache = require('../models/statuses'); + +module.exports = async (id, time) => { + let statuses = await StatusCache.findOne({f: 'lol'}) || new StatusCache({f: 'lol', statuses: []}); + let exists = false; + let status; for (status of statuses.statuses) { + if (status.id === id) {statuses.statuses[statuses.statuses.indexOf(status)].clear = time; exists = true;} + } + if (!exists) {statuses.statuses.push({id: id, clear: time});} + return statuses.save(); +}; \ No newline at end of file diff --git a/util/mention.js b/util/mention.js index dbae079..14ead11 100644 --- a/util/mention.js +++ b/util/mention.js @@ -1,4 +1,6 @@ -const mongooes= require('mongoose'); +const Discord = require('discord.js'); +const moment = require('moment'); + const UserData = require('../models/user'); const GuildSettings = require('../models/guild'); @@ -6,5 +8,10 @@ module.exports = async(message, msg, args, cmd, prefix, mention, client) => { let tu = await UserData.findOne({uid: mention.id}); let tg = message.guild ? await GuildSettings.findOne({gid: message.guild.id}) : null; if (tg && tg.nostatus) {return;} - if (tu) {if (tu.statusmsg.length) {return message.reply(`That user ${tu.statustype === 'dnd' ? 'wishes not to be disturbed' : 'is AFK'}. Reason: ${tu.statusmsg}`);}} + if (client.misc.statusPings.has(message.guild.id) && client.misc.statusPings.get(message.guild.id).has(mention.id) && new Date().getTime() - client.misc.statusPings.get(message.guild.id).get(mention.id).getTime() < 300000) {return;} + if (tu && tu.statusmsg.length) { + if (!client.misc.statusPings.has(message.guild.id)) {client.misc.statusPings.set(message.guild.id, new Discord.Collection());} + client.misc.statusPings.get(message.guild.id).set(mention.id, new Date()); + return message.reply(`That user ${tu.statustype === 'dnd' ? 'wishes not to be disturbed' : 'is AFK'}. Reason: \`${tu.statusmsg}\`. (This status was set ${moment(tu.statussetat.getTime()).fromNow()})`); + } }; \ No newline at end of file diff --git a/util/siftstatuses.js b/util/siftstatuses.js new file mode 100644 index 0000000..7cfe378 --- /dev/null +++ b/util/siftstatuses.js @@ -0,0 +1,25 @@ +const UserData = require('../models/user'); +const StatusCache = require('../models/statuses'); + +module.exports = async function (client, lookFor) { + let statusesm = await StatusCache.findOne({f: 'lol'}) || new StatusCache({f: 'lol', statuses: []}); + let statuses = statusesm.statuses; + let date = new Date(); + let ns = []; + if (!client) {return 'no client found or given';} + let status; for (status of statuses) { + if (date.getTime() > status.clear.getTime()) { + if (lookFor && status.id !== lookFor) {continue;} + let tu = await UserData.findOne({uid: status.id}); + if (tu) { + tu.statusmsg = ''; + tu.statustype = ''; + tu.save(); + let u = await client.users.fetch(status.id); + if (u) {u.send("Heya! Your status has been set for 12 hours, so I've cleared it for you.").catch(() => {});} + } + } else {ns.push(status);} + } + statusesm.statuses = ns; + return statusesm.save(); +}; \ No newline at end of file From 8ba49a228bdf178a7c4389eb4af9c5920bdb21cb Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Mon, 7 Dec 2020 19:04:21 -0700 Subject: [PATCH 36/41] Add ARs and decide.js and patch bugs --- bot.js | 3 ++- commands/eval.js | 6 +++--- commands/reload.js | 13 ++++++++++++- commands/warn.js | 29 +++++++++++++++++------------ events/message.js | 5 +++-- handle/response.js | 16 ++++++++++++++++ models/mod.js | 22 ++++++++++++++++++---- responses/decide.js | 29 +++++++++++++++++++++++++++++ 8 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 handle/response.js create mode 100644 responses/decide.js diff --git a/bot.js b/bot.js index 65e3280..bfdec62 100644 --- a/bot.js +++ b/bot.js @@ -10,7 +10,8 @@ async function init() { client.config = auth; ['commands', 'aliases'].forEach(x => client[x] = new Discord.Collection()); - ['command', 'event'].forEach(x => require(`./handle/${x}`)(client)); + client.responses = {triggers: [], commands: new Discord.Collection()}; + ['command', 'event', 'response'].forEach(x => require(`./handle/${x}`)(client)); client.developers = ["330547934951112705", "673477059904929802"]; diff --git a/commands/eval.js b/commands/eval.js index cfdfa58..7bc4949 100644 --- a/commands/eval.js +++ b/commands/eval.js @@ -13,18 +13,18 @@ module.exports = { const result = new Promise((resolve) => resolve(eval(args.join(' ')))); return result.then((output) => { if (typeof output !== 'string') { - output = require('util').inspect(output, { depth: 0 }); + output = require('util').inspect(output, {depth: 0}); } output = output.replace(client.config.token, 'Client Token') .replace(client.config.database.password, 'Database Password') - .replace(client.config.database.cluster, 'Database Cluster') + .replace(client.config.database.cluster, 'Database Cluster'); return message.channel.send(new Discord.MessageEmbed() .setTitle('Client Evaluation') .setDescription(`\`\`\`js\n${output}\n\`\`\``) .setColor('c375f0') .setFooter(`Natsuki`, client.user.avatarURL()) - .setTimestamp()) + .setTimestamp()); }).catch(error => {return message.channel.send(`Error: \`${error}\`.`);}); } catch (error) { let date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6); diff --git a/commands/reload.js b/commands/reload.js index 9793385..a76eb8e 100644 --- a/commands/reload.js +++ b/commands/reload.js @@ -39,13 +39,24 @@ module.exports = { } console.log(`${chalk.gray('[LOG]')} >> ${chalk.blue('Loaded all Events')}`); + var responses = fs.readdirSync('./responses').filter(file => file.endsWith('.js')); + client.responses.triggers = []; + for (let responsef of responses) { + if (Object.keys(require.cache).includes(require.resolve(`../responses/${responsef}`))) {delete require.cache[require.resolve(`../responses/${responsef}`)];} + var response = require(`../responses/${responsef}`); + client.responses.triggers.push([response.name, response.condition]); + client.responses.commands.set(response.name, response); + } + console.log(`${chalk.gray('[LOG]')} >> ${chalk.blue('Loaded all Responses')}`); + console.log(`\n${chalk.gray('[INFO]')} >> ${chalk.hex('ff4fd0')(`Client refresh successful`)}\n`); return message.channel.send("Done!") } if (['l', 'log', 'ns', 'nosilent', 'notsilent'].includes(args[0].toLowerCase())) { ['commands', 'aliases'].forEach(x => client[x] = new Discord.Collection()); - ['command', 'event'].forEach(x => require(`../handle/${x}`)(client)); + client.responses = {triggers: [], commands: new Discord.Collection()}; + ['command', 'event', 'response'].forEach(x => require(`./${x}`)(client)); return message.channel.send("Done!"); } else {return message.channel.send("Oi! 'log' is the only valid arg to use. Use no args if you want a cleaner console output instead.");} diff --git a/commands/warn.js b/commands/warn.js index c2c1f2a..f70e18c 100644 --- a/commands/warn.js +++ b/commands/warn.js @@ -24,21 +24,20 @@ module.exports = { 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 || !Object.keys(mh.warnings).length) {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); + if (!mh.warnings[user.id] || !mh.warnings[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); + let warning; for (warning of mh.warnings[user.id]) { + let tcase = mh.cases[warning - 1]; 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.");} + if (cwc === mh.warnings[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})) @@ -97,7 +96,7 @@ module.exports = { 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}`, { + mhcases.push({ members: [user.id], punishment: "Warned", reason: reason, @@ -109,14 +108,20 @@ module.exports = { }); let mhwarnings = mh.warnings; + let mhwarningsk = Object.keys(mhwarnings); + 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]); + + if (mhwarningsk.includes(user.id)) {let tw = mhwarnings[user.id]; tw.push(mhcases.length); mhwarnings[user.id] = tw;} + else {mhwarnings[user.id] = [mhcases.length];} + + console.log(mhwarnings); + mh.warnings = mhwarnings; + mh.warnings[user.id] = mhwarnings[user.id]; mh.cases = mhcases; - if (!options.silent) {message.channel.send(`Case ${mh.cases.size} - Member has been warned. Reason: \`${reason}\``);} + if (!options.silent) {message.channel.send(`Case ${mh.cases.length} - 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(); diff --git a/events/message.js b/events/message.js index b58e5ad..a0220fa 100644 --- a/events/message.js +++ b/events/message.js @@ -49,13 +49,14 @@ module.exports = async (client, message) => { try { if (msg.startsWith(prefix) || msg.startsWith(`<@${client.user.id}>`) || msg.startsWith(`<@!${client.user.id}>`)) { let command = client.commands.get(cmd) || client.commands.get(client.aliases.get(cmd)); - if (!command) {return;} + if (!command) {let trigger; for (trigger of client.responses.triggers) {if (await trigger[1](message, msg, args, cmd, prefix, mention, client)) {await client.responses.commands.get(trigger[0]).execute(message, msg, args, cmd, prefix, mention, client); break;}} return;} message.channel.startTyping(); await wait(800); message.channel.stopTyping(); require('../util/oncommand')(message, msg, args, cmd, prefix, mention, client); - command.execute(message, msg, args, cmd, prefix, mention, client); + return command.execute(message, msg, args, cmd, prefix, mention, client); } + let trigger; for (trigger of client.responses.triggers) {if (await trigger[1](message, msg, args, cmd, prefix, mention, client)) {await client.responses.commands.get(trigger[0]).execute(message, msg, args, cmd, prefix, mention, client); break;}} } catch (e) { var date = new Date; date = date.toString().slice(date.toString().search(":") - 2, date.toString().search(":") + 6); console.error(`\n${chalk.red('[ERROR]')} >> ${chalk.yellow(`At [${date}] | In ${message.guild.name}\n`)}`, e); diff --git a/handle/response.js b/handle/response.js new file mode 100644 index 0000000..1ad9f94 --- /dev/null +++ b/handle/response.js @@ -0,0 +1,16 @@ +const Discord = require('discord.js'); +const fs = require('fs'); +const chalk = require('chalk'); + +module.exports = client => { + var responses = fs.readdirSync('./responses').filter(file => file.endsWith('.js')); + console.log(`\n${chalk.gray('[BOOT]')} >> ${chalk.blue('Getting Responses...')}\n`); + for (let responsef of responses) { + if (Object.keys(require.cache).includes(require.resolve(`../responses/${responsef}`))) {delete require.cache[require.resolve(`../responses/${responsef}`)];} + var response = require(`../responses/${responsef}`); + client.responses.triggers.push([response.name, response.condition]); + client.responses.commands.set(response.name, response); + console.log(`${chalk.gray('[LOG] ')} >> ${chalk.blueBright('Loaded Response')} ${chalk.white(response.name)}`); + } + console.log(`\n${chalk.gray('[BOOT]')} >> ${chalk.blue('Loaded all Responses')}`); +}; \ No newline at end of file diff --git a/models/mod.js b/models/mod.js index b248801..9d07e15 100644 --- a/models/mod.js +++ b/models/mod.js @@ -2,10 +2,24 @@ 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()}, + cases: {type: [{ + members: [String], + punishment: String, + reason: String, + status: String, + moderators: [String], + notes: [String], + history: [String], + issued: Date + }], default: []}, + rules: {type: [{ + name: String, + description: String, + punishment: String, + automod: String + }], default: []}, + auto: {type: Object, default: {}}, + warnings: {type: Object, default: {}}, maxWarnings: {type: Number, default: 0}, onMaxWarn: {type: String, default: 'kick'} }); diff --git a/responses/decide.js b/responses/decide.js new file mode 100644 index 0000000..e484b6b --- /dev/null +++ b/responses/decide.js @@ -0,0 +1,29 @@ +const Discord = require('discord.js'); + +module.exports = { + name: "decide", + meta: { + category: "", + perms: "", + staff: false, + vip: "", + serverPerms: [], + writtenBy: "", + serverOnly: false + }, + tags: [], + help: new Discord.MessageEmbed() + .setTitle("Help -> ") + .setDescription("") + .addField("Syntax", "``"), + async condition (message, msg, args, cmd, prefix, mention, client) {return msg.split(/\s+/gm).length > 3 && (msg.startsWith(`<@${client.user.id}>`) || msg.startsWith(`<@!${client.user.id}>`));}, + async execute(message, msg, args, cmd, prefix, mention, client) { + let e = message.content.split(/\s+/g); e.shift(); + let options = e.join(" ").split(/(?:(?:\s+)[oO][rR] [sS][hH][oO][uU][lL][dD] [iI](?:\s+)|,(?:\s*)[oO][rR] [sS][hH][oO][uU][lL][dD] [iI](?:\s+)|(?:\s+)[oO][rR] [dD][oO] [iI](?:\s+)|,(?:\s*)[dD][oO] [iI](?:\s+)|,(?:\s*)[sS][hH][oO][uU][lL][dD] [iI](?:\s+)|,(?:\s*)[oO][rR] [dD][oO] [iI](?:\s+)|,(?:\s*)[oO][rR](?:\s+)|(?:\s+)[oO][rR](?:\s+)|,(?:\s*))/gm); + //console.log(e, options); + if (options.length < 2) {return;} + let cleanups = ['should i ', 'do i ']; + let option; for (option of options) {let c; for (c of cleanups) {if (option.trim().toLowerCase().startsWith(c)) {options[options.indexOf(option)] = option.trim().slice(c.length);}}} + return message.channel.send(`${['You should', 'How about', 'Hmmm... I pick', 'How about you', 'I think you should'][Math.floor(Math.random() * 5)]} ${options[Math.floor(Math.random() * options.length)]}.`); + } +}; \ No newline at end of file From dc4fa00fd26f36a1416cebca5aefcbabb9a3c6b4 Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Mon, 7 Dec 2020 22:40:56 -0700 Subject: [PATCH 37/41] Logs -> Setting, caching, mdelete logs --- bot.js | 1 + commands/anime.js | 32 +++++++++++++------- commands/logs.js | 67 +++++++++++++++++++++++++++++++++++++---- events/messageDelete.js | 27 +++++++++++++++++ events/ready.js | 15 +++++++-- models/anime.js | 3 +- models/log.js | 50 +++++++++++++++--------------- 7 files changed, 150 insertions(+), 45 deletions(-) create mode 100644 events/messageDelete.js diff --git a/bot.js b/bot.js index bfdec62..66d88ae 100644 --- a/bot.js +++ b/bot.js @@ -26,5 +26,6 @@ async function init() { client.guildconfig = {}; client.guildconfig.prefixes = new Map(); + client.guildconfig.logs = new Map(); } init(); \ No newline at end of file diff --git a/commands/anime.js b/commands/anime.js index ad9edf0..cb6bde8 100644 --- a/commands/anime.js +++ b/commands/anime.js @@ -36,20 +36,30 @@ module.exports = { new Tag(['genres', 'g'], 'genres', 'listAppend'), new Tag(['tags', 'ta', 'tgs', 'tg', 'tag'], 'tags', 'listAppend'), new Tag(['cs', 'characters', 'chars', 'chs'], 'characters', 'listAppend'), - new Tag(['streams', 'streamat', 'sa'], 'streamAt', 'listAppend') + new Tag(['streams', 'streamat', 'sa'], 'streamAt', 'listAppend'), + new Tag(['img', 'thumb', 'thumbnail', 'image']) ]).test(args.join(' ')); - - var foptions = {}; - let option; for (option of Object.keys(options)) { - if (Array.isArray(options[option])) { - let s = ''; - let data; - for (data of options[option]) { - s += data; - s += options[option].indexOf(data) < (options[option].length - 1) ? ', ' : ''; + + if (Object.keys(options).length) { + var foptions = {}; + let option; for (option of Object.keys(options)) { + if (Array.isArray(options[option])) { + let s = ''; + let data; + for (data of options[option]) { + s += data; + s += options[option].indexOf(data) < (options[option].length - 1) ? ', ' : ''; + } + foptions[option] = s; } - foptions[option] = s; } + } else { + if (client.misc.activeDMs.has(message.author.id)) {return message.channel.send("I'm already asking you questions in a DM! Finish that first, then try this command again.");} + client.misc.activeDMs.set(message.author.id, 'anime-add'); + + await message.author.send("I'm going to ask you some questions about the anime's info. Just reply with the answer and use good grammar and spelling and be as accurate as possible. To cancel the process, just leave the question unanswered for a few minutes and I'll let you know that the question timed out and is not longer answerable.") + .catch(() => {return message.reply("Something went wrong there! Most likely, your DMs are closed.");}); + } message.channel.send(new Discord.MessageEmbed() diff --git a/commands/logs.js b/commands/logs.js index 042fe2e..a43af1f 100644 --- a/commands/logs.js +++ b/commands/logs.js @@ -1,17 +1,72 @@ const Discord = require("discord.js"); +const GuildData = require('../models/guild'); +const LogData = require('../models/log'); + + +const ObjLogTypes = { + mdelete: ['md', 'messagedelete', 'deletemessage', 'deletemsg', 'msgdelete'], + medit: ['me', 'messageedit', 'editmessage', 'msgedit', 'editmsg'], + chnew: ['chn', 'chc', 'newch', 'newchannel', 'chcreate', 'channelcreate'], + //chedit: ['channeledit'], + chdelete: ['chd', 'channeldelete', 'deletechannel', 'deletech', 'chdelete'], + //vcjoin: [], + //vcleave: [], + //servervcmute: [], + //servervcdeafen: [], + //kick: [], + //ban: [], + //mute: [], + //warn: [], + //giverole: [], + //takerole: [], + //addrole: [], + //editrole: [], + //deleterole: [], + //serverjoin: [], + //serverleave: [], + //nickname: [], + //username: [], + //avatar: [] +}; const LogTypes = new Map(); + +let keys = Object.keys(ObjLogTypes); +let key; for (key of keys) {let vs = ObjLogTypes[key]; let v; for (v of vs) {LogTypes.set(v, key);}} + + module.exports = { name: "logs", aliases: ["log", "l", "modlog", "modlogs"], help: new Discord.MessageEmbed() .setTitle("Help -> Server Logs") - .setDescription("Comfigure your server's log settings.\n\nLogs will update you on what ") - .addField("Syntax", "`vip `") - .addField("Notice", "This command is **developer-only**."), + .setDescription("Configure your server's log settings.\n\nLogs will update you on events in your server that have the potential to require moderator intervention, like someone deleting a hateful message before you can see it or a misbehaving moderator kicking/banning a member when they aren't supposed to.") + .addField("Syntax", "`log [logType] [#channel]`") + .addField("Notice", "You must be an admin or have the specified staff role in order to use this command."), async execute(message, msg, args, cmd, prefix, mention, client) { if (!message.guild) {return message.reply("This command is server-only!");} - if (!args.length) {return message.channel.send(`Syntax: \`${prefix}vip \``);} - const GuildSettings = require('../models/guild'); - + let tg = await GuildData.findOne({gid: message.guild.id}); + if ((!message.member.permissions.has("ADMINISTRATOR")) && (!tg || !tg.staffrole || !tg.staffrole.length || !message.member.roles.cache.has(tg.staffrole))) {return message.reply("You must be an administrator or have the specified staff role in this server in order to edit or view log settings.");} + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}log [logType] [#channel]\``);} + + if (['s', 'set'].includes(args[0].toLowerCase())) { + if (args.length < 3) {return message.channel.send(`You must specify the log type and the channel to send the log to. Use \`${prefix}log list\` to see a list of valid log types.`);} + if (!LogTypes.has(args[1].toLowerCase())) {return message.channel.send("That's not a valid log type. Use \`${prefix}log list\` to see a list of valid log types.");} + let lt = LogTypes.get(args[1].toLowerCase()); + let ch = args[2].match(/<\#(?:\d+)>/m) && message.guild.channels.cache.has(message.mentions.channels.first().id) ? message.mentions.channels.first() : message.guild.channels.cache.has(args[2]) ? message.guild.channels.cache.get(args[2]) : null; + if (!ch) {return message.channel.send("I can't find that channel! Make sure that you've mentioned one, or that the ID you provided is correct, and that I can see it.");} + if (!ch.permissionsFor(client.user.id).has("SEND_MESSAGES")) {return message.reply("I don't have permissions to send messages in that channel. Please give me access and try again.");} + let tl = await LogData.findOne({gid: message.guild.id}) || new LogData({gid: message.guild.id}); + tl[lt] = ch.id; + tl.save(); + if (!client.guildconfig.logs.has(message.guild.id)) {client.guildconfig.logs.set(message.guild.id, new Map());} + client.guildconfig.logs.get(message.guild.id).set(lt, ch.id); + return message.channel.send("Log settings updated!"); + } + + if (['l', 'list'].includes(args[0].toLowerCase())) {} + + if (['v', 'view'].includes(args[0].toLowerCase())) {} + + if (['c', 'clear'].includes(args[0].toLowerCase())) {} } }; \ No newline at end of file diff --git a/events/messageDelete.js b/events/messageDelete.js new file mode 100644 index 0000000..cf01bf4 --- /dev/null +++ b/events/messageDelete.js @@ -0,0 +1,27 @@ +const Discord = require('discord.js'); + +module.exports = async (client, message) => { + if (message.channel.type != "text") {return;}; + //if (!Object.keys(snipe.delete).includes(message.guild.id)) {snipe.delete[message.guild.id] = {};}; + //snipe.delete[message.guild.id][message.channel.id] = message; + + let ts = client.guildconfig.logs.has(message.guild.id) && client.guildconfig.logs.get(message.guild.id).has('mdelete') ? client.guildconfig.logs.get(message.guild.id).get('mdelete') : null; + if (ts) {if (message.guild.channels.cache.has(ts) && message.guild.channels.cache.get(ts).permissionsFor(client.user.id).has("SEND_MESSAGES")) { + let mde = new Discord.MessageEmbed() + .setTitle('Message Deleted') + .setDescription(`Sent by <@${message.author.id}> | In <#${message.channel.id}>`) + .setThumbnail(message.author.avatarURL({size: 1024})) + .setColor('ecff8f').setFooter("Natsuki", client.user.avatarURL()).setTimestamp(); + if (message.content && message.content.length) {mde.addField("Message", "`-> `" + message.content.toString());} + if (message.attachments.size) { + if (message.attachments.first().url.includes(".png") || message.attachments.first().url.includes(".jpg") || message.attachments.first().url.includes(".gif")) {/*console.log('e');*/ try {mde.setImage(message.attachments.first().url);} catch {}} + let av = Array.from(message.attachments.values()); + as = ''; for (let a of av) { + as += `[Att. ${av.indexOf(a) + 1}](${a.url})`; + if (av.indexOf(a) + 1 < av.length) {as += ' | ';} + } + if (as.length) {mde.addField('Attachments', as);} + } + message.guild.channels.cache.get(ts).send(mde); + }} +} \ No newline at end of file diff --git a/events/ready.js b/events/ready.js index c0a7818..ea9502d 100644 --- a/events/ready.js +++ b/events/ready.js @@ -5,6 +5,7 @@ const mongoose = require('mongoose'); const GuildSettings = require('../models/guild'); const BotDataSchema = require('../models/bot'); +const LogData = require('../models/log'); const siftStatuses = require('../util/siftstatuses'); @@ -56,14 +57,22 @@ module.exports = async client => { setInterval(setR, 14400000); - const setP = async () => {let tg; for (tg of Array.from(client.guilds.cache.values)) { + const setPL = async () => {let tg; for (tg of Array.from(client.guilds.cache.values)) { let tguild = await GuildSettings.findOne({gid: tg.id}); if (tguild && tguild.prefix && tguild.prefix.length) {client.guildconfig.prefixes.set(tg.id, tguild.prefix);} + let tl = await LogData.findOne({gid: tg.id}); + if (tl) { + let keys = Object.keys(tl); + let k; for (k of keys) {if (typeof tl[k] === "string" && tl[k].length) { + if (!client.guildconfig.logs.has(tg.id)) {client.guildconfig.logs.set(tg.id, new Map());} + client.guildconfig.logs.get(tg.id).set(k, tl[k]); + }} + } }}; - setP(); + setPL(); siftStatuses(); - setInterval(() => {setP(); siftStatuses(client, null);}, 120000); + setInterval(() => {setPL(); siftStatuses(client, null);}, 120000); let botData = await BotDataSchema.findOne({finder: 'lel'}) ? await BotDataSchema.findOne({finder: 'lel'}) diff --git a/models/anime.js b/models/anime.js index 44226e9..ab0d902 100644 --- a/models/anime.js +++ b/models/anime.js @@ -20,7 +20,8 @@ const AniSchema = new mongoose.Schema({ listed: Number, liked: Number, rating: Number, - lastUpdate: Date + lastUpdate: Date, + thumbnail: String }); module.exports = mongoose.model('anime', AniSchema); \ No newline at end of file diff --git a/models/log.js b/models/log.js index 7425e29..6afb660 100644 --- a/models/log.js +++ b/models/log.js @@ -2,27 +2,29 @@ const mongoose = require('mongoose'); const logSchema = new mongoose.Schema({ gid: {type: String, unique: true}, - mdelete: {type: Boolean, default: true}, - medit: {type: Boolean, default: true}, - chnew: {type: Boolean, default: true}, - chedit: {type: Boolean, default: true}, - chdelete: {type: Boolean, default: true}, - vcjoin: {type: Boolean, default: false}, - vcleave: {type: Boolean, default: false}, - servervcmute: {type: Boolean, default: true}, - servervcdeafen: {type: Boolean, default: true}, - kick: {type: Boolean, default: true}, - ban: {type: Boolean, default: true}, - mute: {type: Boolean, default: true}, - warn: {type: Boolean, default: true}, - giverole: {type: Boolean, default: true}, - takerole: {type: Boolean, default: true}, - addrole: {type: Boolean, default: true}, - editrole: {type: Boolean, default: true}, - deleterole: {type: Boolean, default: true}, - serverjoin: {type: Boolean, default: false}, - serverleave: {type: Boolean, default: false}, - nickname: {type: Boolean, default: true}, - username: {type: Boolean, default: false}, - avatar: {type: Boolean, default: false}, -}); \ No newline at end of file + mdelete: {type: String, default: ''}, + medit: {type: String, default: ''}, + chnew: {type: String, default: ''}, + chedit: {type: String, default: ''}, + chdelete: {type: String, default: ''}, + vcjoin: {type: String, default: ''}, + vcleave: {type: String, default: ''}, + servervcmute: {type: String, default: ''}, + servervcdeafen: {type: String, default: ''}, + kick: {type: String, default: ''}, + ban: {type: String, default: ''}, + mute: {type: String, default: ''}, + warn: {type: String, default: ''}, + giverole: {type: String, default: ''}, + takerole: {type: String, default: ''}, + addrole: {type: String, default: ''}, + editrole: {type: String, default: ''}, + deleterole: {type: String, default: ''}, + serverjoin: {type: String, default: ''}, + serverleave: {type: String, default: ''}, + nickname: {type: String, default: ''}, + username: {type: String, default: ''}, + avatar: {type: String, default: ''} +}); + +module.exports = mongoose.model('log', logSchema); \ No newline at end of file From 81f1d78522f0fce17f0f9e820bf3581d246b559d Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Tue, 8 Dec 2020 11:12:46 -0700 Subject: [PATCH 38/41] Message edit logs --- events/messageDelete.js | 2 +- events/messageUpdate.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 events/messageUpdate.js diff --git a/events/messageDelete.js b/events/messageDelete.js index cf01bf4..5fc5f7a 100644 --- a/events/messageDelete.js +++ b/events/messageDelete.js @@ -22,6 +22,6 @@ module.exports = async (client, message) => { } if (as.length) {mde.addField('Attachments', as);} } - message.guild.channels.cache.get(ts).send(mde); + message.guild.channels.cache.get(ts).send(mde).catch(() => {}); }} } \ No newline at end of file diff --git a/events/messageUpdate.js b/events/messageUpdate.js new file mode 100644 index 0000000..89a942b --- /dev/null +++ b/events/messageUpdate.js @@ -0,0 +1,22 @@ +const Discord = require('discord.js'); + +module.exports = async (client, oldM, newM) => { + if (oldM.channel.type != "text") {return;}; + if (oldM.author.bot) {return;} + if (oldM.deleted) {return;} + //if (!Object.keys(snipe.edit).includes(oldM.guild.id)) {snipe.edit[oldM.guild.id] = {};}; + //snipe.edit[oldM.guild.id][oldM.channel.id] = {old: oldM, cur: newM}; + + let ts = client.guildconfig.logs.has(oldM.guild.id) && client.guildconfig.logs.get(oldM.guild.id).has('medit') ? client.guildconfig.logs.get(oldM.guild.id).get('medit') : null; + if (ts) {if (oldM.guild.channels.cache.has(ts) && oldM.guild.channels.cache.get(ts).permissionsFor(client.user.id).has("SEND_oldMS")) { + let embed = new Discord.MessageEmbed() + .setTitle('Message Edited') + .setDescription(`Sent by <@${oldM.author.id}> | In <#${oldM.channel.id}> | [See Message](${oldM.url})`) + .setThumbnail(oldM.author.avatarURL({size: 1024})) + .addField("Old Message", "`-> `" + oldM.content.toString()) + .addField("New Message", "`-> `" + newM.content.toString()) + .setColor('8034eb').setFooter("Natsuki", client.user.avatarURL()).setTimestamp(); + if (newM.attachments.size && ['.png', '.jpg', '.gif'].includes(newM.attachments.first().url.slice(newM.attachments.first().url.length - 4, newM.attachments.first().url.length))) {embed.setImage(newM.attachments.first().url);} + oldM.guild.channels.cache.get(ts).send(embed).catch(() => {}); + }} +} \ No newline at end of file From f9da99060f055e0abe8db9ad3df090783a23840d Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Wed, 9 Dec 2020 12:16:35 -0700 Subject: [PATCH 39/41] updates --- commands/logs.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/commands/logs.js b/commands/logs.js index a43af1f..ae33f3b 100644 --- a/commands/logs.js +++ b/commands/logs.js @@ -5,8 +5,8 @@ const LogData = require('../models/log'); const ObjLogTypes = { - mdelete: ['md', 'messagedelete', 'deletemessage', 'deletemsg', 'msgdelete'], - medit: ['me', 'messageedit', 'editmessage', 'msgedit', 'editmsg'], + mdelete: ['md', 'mdelete', 'messagedelete', 'deletemessage', 'deletemsg', 'msgdelete'], + medit: ['me', 'medit', 'messageedit', 'editmessage', 'msgedit', 'editmsg'], chnew: ['chn', 'chc', 'newch', 'newchannel', 'chcreate', 'channelcreate'], //chedit: ['channeledit'], chdelete: ['chd', 'channeldelete', 'deletechannel', 'deletech', 'chdelete'], @@ -63,10 +63,17 @@ module.exports = { return message.channel.send("Log settings updated!"); } - if (['l', 'list'].includes(args[0].toLowerCase())) {} + if (['l', 'list'].includes(args[0].toLowerCase())) { + return message.channel.send("Valid log types:\n\n-`msgdelete` - Shows the content of a message that was deleted, in any channel.\n-`msgedit` - Shows both the old and new versions of a message when it is edited."); + } - if (['v', 'view'].includes(args[0].toLowerCase())) {} + if (['v', 'view'].includes(args[0].toLowerCase())) { + if (client.guildconfig.logs.has(message.guild.id) && client.guildconfig.logs.get(message.guild.id).size) {return message.channel.send(`This server's logs: \n\n${function bonk(){let s = ''; Array.from(client.guildconfig.logs.get(message.guild.id).keys()).forEach(v => s+=`\`${v}\`: <#${client.guildconfig.logs.get(message.guild.id).get(v)}>, `); return s;}().slice(0, -2)}`);} + else {return message.channel.send("Your server doesn't have any logs set up at the moment, or they aren't cached. If you keep seeing this issue even after setting logs, please contact my developers!");} + } - if (['c', 'clear'].includes(args[0].toLowerCase())) {} + if (['c', 'clear'].includes(args[0].toLowerCase())) { + + } } }; \ No newline at end of file From 40db8704da1bdb7af7df27e3665adf2ede9736ef Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Thu, 10 Dec 2020 18:49:06 -0700 Subject: [PATCH 40/41] Polished eval and de-cached statuses on clear --- commands/clearstatus.js | 2 ++ commands/eval.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/commands/clearstatus.js b/commands/clearstatus.js index 61a3681..4692d56 100644 --- a/commands/clearstatus.js +++ b/commands/clearstatus.js @@ -1,5 +1,6 @@ const Discord = require('discord.js'); const mongooes = require('mongoose'); + const UserData = require('../models/user'); module.exports = { @@ -15,6 +16,7 @@ module.exports = { tu.statusmsg = ''; tu.statustype = ''; tu.save(); + require('../util/siftstatuses')(client, message.author.id); return message.reply("welcome back! I cleared your status."); } }; \ No newline at end of file diff --git a/commands/eval.js b/commands/eval.js index 7bc4949..0e3c8af 100644 --- a/commands/eval.js +++ b/commands/eval.js @@ -1,15 +1,22 @@ const Discord = require('discord.js'); const chalk = require('chalk'); +const {Tag} = require('../util/tag'); +const {TagFilter} = require('../util/tagfilter'); + module.exports = { name: 'eval', aliases: ['ev', ':', 'e'], help: "Evaluates raw JavaScript code. *This is a __developer-only__ command.* Usage: `{{p}}eval `", execute(message, msg, args, cmd, prefix, mention, client) { try { - if (!client.developers.includes(message.author.id)) return; + if (!client.developers.includes(message.author.id)) {return message.channel.send("Sorry, but I've got trust issues, so only me devs can go commanding me around like that.");}; + if (!args.length) return message.channel.send(`Syntax: \`${prefix}eval \``); + + let options = new TagFilter([new Tag(['s', 'silent', 'nr', 'noreply'], 'silent', 'toggle')]).test(args[0]); + if (options.silent) {args.shift();} - if (!args.length) return message.channel.send(`Syntax: \`${prefix}eval \``); + if (!args.length) {return message.channel.send("Silly goose, if you want me to do something, you have to tell me what!");} const result = new Promise((resolve) => resolve(eval(args.join(' ')))); return result.then((output) => { if (typeof output !== 'string') { @@ -19,7 +26,7 @@ module.exports = { .replace(client.config.database.password, 'Database Password') .replace(client.config.database.cluster, 'Database Cluster'); - return message.channel.send(new Discord.MessageEmbed() + return options.silent ? null : message.channel.send(new Discord.MessageEmbed() .setTitle('Client Evaluation') .setDescription(`\`\`\`js\n${output}\n\`\`\``) .setColor('c375f0') From 18e28bdde40950a79b52f4904b48421eb7ec671d Mon Sep 17 00:00:00 2001 From: WubzyGD Date: Thu, 10 Dec 2020 23:53:16 -0700 Subject: [PATCH 41/41] Warnings fixed and added n?clearwarn --- commands/clearwarnings.js | 62 +++++++++++++++++++++++++++++++++++++++ commands/warn.js | 25 +++++++++------- 2 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 commands/clearwarnings.js diff --git a/commands/clearwarnings.js b/commands/clearwarnings.js new file mode 100644 index 0000000..d50dbb0 --- /dev/null +++ b/commands/clearwarnings.js @@ -0,0 +1,62 @@ +const Discord = require('discord.js'); + +const Mod = require('../models/mod'); + +module.exports = { + name: "clearwarnings", + aliases: ['clearwarn', 'cw', 'warnclear', 'wc', 'clearwarning'], + meta: { + category: "", + perms: "", + staff: false, + vip: "", + serverPerms: [], + writtenBy: "", + serverOnly: false + }, + tags: [], + help: new Discord.MessageEmbed() + .setTitle("Help -> Warn Clearing") + .setDescription("Clears the warnigns of a user") + .addField("Syntax", "`clearwarnings <@user|userID>`") + .addField("Notice", "You must be a server administrator in order to use this command."), + async execute(message, msg, args, cmd, prefix, mention, client) { + if (!args.length) {return message.channel.send(`Syntax: \`${prefix}clearwarnings <@user|userID>\``);} + if (!message.guild) {return message.channel.send("This is a server-only command.");} + if (!message.member.permissions.has("ADMINISTRATOR")) {return message.reply("You must be a server administrator to use this command.");} + + 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) {return message.channel.send("Either you didn't mention a user, or I can't find that user!");} + + if (user.id === client.user.id) {return message.reply("don't worry about clearing any warnings from me... you can't give me warnings in the first place");} + if (client.users.cache.get(user.id).bot) {return message.reply("it's not like a bot would have any warnings in the first place...");} + + user = user ? user : message.member; + let mh = await Mod.findOne({gid: message.guild.id}); + if (!mh || !Object.keys(mh.warnings).length) {return message.reply("There are no warnings available in this server.");} + + if (!Object.keys(mh.warnings).includes(user.id) || !mh.warnings[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 moddedcases = []; + let cwc = 0; var wc = 0; + let warning; for (warning of mh.warnings[user.id]) { + if (mhcases[`${warning - 1}`].status !== "Cleared") { + let tcase = mhcases[`${warning - 1}`]; + tcase.status = "Cleared"; + tcase.history.push(`${new Date().toISOString()} - ${message.author.username} - Cleared the warning.`); + moddedcases.push(`${warning - 1}`); + wc++; + if (!tcase.moderators.includes(message.author.id)) {tcase.moderators.push(message.author.id);} + mhcases[`${warning - 1}`] = tcase; + } else {cwc++;} + } + if (cwc === mh.warnings[user.id].length) {return message.reply("That user has no uncleared warnings.");} + + if (moddedcases.length) {let c; for (c of moddedcases) {mh.markModified(`cases.${c}.history`);}} + + mh.cases = mhcases; + mh.save(); + return message.reply(`Cleared ${wc} warnings from ${user.displayName}.`); + } +}; \ No newline at end of file diff --git a/commands/warn.js b/commands/warn.js index f70e18c..3b76ece 100644 --- a/commands/warn.js +++ b/commands/warn.js @@ -1,4 +1,5 @@ const Discord = require('discord.js'); + const Mod = require('../models/mod'); const {TagFilter} = require('../util/tagfilter'); @@ -52,23 +53,27 @@ module.exports = { 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 || !Object.keys(mh.warnings).length) {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.`);} + if (!Object.keys(mh.warnings).includes(user.id) || !mh.warnings[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 moddedcases = []; 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}`); + let warning; for (warning of mh.warnings[user.id]) { + if (mhcases[`${warning - 1}`].status !== "Cleared") { + let tcase = mhcases[`${warning - 1}`]; tcase.status = "Cleared"; tcase.history.push(`${new Date().toISOString()} - ${message.author.username} - Cleared the warning.`); + moddedcases.push(`${warning - 1}`); wc++; if (!tcase.moderators.includes(message.author.id)) {tcase.moderators.push(message.author.id);} - mhcases.set(`${warning}`, tcase); + mhcases[`${warning - 1}`] = tcase; } else {cwc++;} } - if (cwc === mh.warnings.get(user.id).length) {return message.reply("That user has no uncleared warnings.");} + if (cwc === mh.warnings[user.id].length) {return message.reply("That user has no uncleared warnings.");} + + if (moddedcases.length) {let c; for (c of moddedcases) {mh.markModified(`cases.${c}.history`);}} mh.cases = mhcases; mh.save(); @@ -110,19 +115,17 @@ module.exports = { let mhwarnings = mh.warnings; let mhwarningsk = Object.keys(mhwarnings); - console.log(mhwarnings); - if (mhwarningsk.includes(user.id)) {let tw = mhwarnings[user.id]; tw.push(mhcases.length); mhwarnings[user.id] = tw;} else {mhwarnings[user.id] = [mhcases.length];} - console.log(mhwarnings); - mh.warnings = mhwarnings; mh.warnings[user.id] = mhwarnings[user.id]; mh.cases = mhcases; if (!options.silent) {message.channel.send(`Case ${mh.cases.length} - 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.markModified(`warnings.${user.id}`); mh.save(); return null;