diff --git a/.idea/Natsuki.iml b/.idea/Natsuki.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/Natsuki.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c4ee72c
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/commands/avatar.js b/commands/avatar.js
new file mode 100644
index 0000000..8f474cc
--- /dev/null
+++ b/commands/avatar.js
@@ -0,0 +1,26 @@
+const Discord = require('discord.js');
+const {Tag} = require('../util/tag');
+const {TagFilter} = require('../util/tagfilter');
+
+module.exports = {
+ name: "avatar",
+ aliases: ['av', 'a', 'pfp'],
+ help: "Use `{{p}}avatar` to get your own profile picture, or mention someone to get theirs!",
+ async execute(message, msg, args, cmd, prefix, mention, client) {
+ let member = !args.length ? message.author : mention ? mention : client.users.cache.has(args[0]) ? client.users.cache.get(args[0]) : message.author;
+ let name = !args.length ? message.member ? message.member.displayName : message.author.username : mention ? mention.username : client.users.cache.has(args[0]) ? client.users.cache.get(args[0]).username : message.author.username;
+ let options = new TagFilter([
+ new Tag(['small', 's', 'mini', 'm'], 'small', 'toggle'),
+ new Tag(['verysmall', 'vsmall', '-vs', 'xs'], 'vsmall', 'toggle')
+ ]).test(args.join(" "));
+ try {
+ let avem = new Discord.MessageEmbed()
+ .setTitle(`${name.endsWith('s') ? `${name}'` : `${name}'s`} Avatar`)
+ .setImage(member.avatarURL({size: options.vsmall ? 128 : options.small ? 256 : 2048}))
+ .setColor('c375f0')
+ .setFooter("Natsuki", client.user.avatarURL())
+ if (!options.vsmall) {avem.setTimestamp();}
+ return message.channel.send(avem);
+ } catch {return message.reply("Hmm, there seems to have been an error while I tried to show you that user's avatar.");}
+ }
+};
\ No newline at end of file
diff --git a/commands/deathnote.js b/commands/deathnote.js
index c7ce72f..6e23da1 100644
--- a/commands/deathnote.js
+++ b/commands/deathnote.js
@@ -1,5 +1,7 @@
const Discord = require('discord.js');
const moment = require('moment');
+const {Tag} = require('../util/tag');
+const {TagFilter} = require('../util/tagfilter');
const deaths = [
"watching too much anime", "an overdose of waifus", "Hypotakunemia", "trying to self-isekai",
@@ -58,20 +60,38 @@ module.exports = {
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 (!mention) {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.)");}
- if (!args[0].trim().match(/^<@(?:\!?)\d+>$/)) {return message.reply("You have to mention someone!");}
- if (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.id == client.user.id) {return message.reply("You can't kill me! Little did you know, I'm actually a death god!");}
+ //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!");}
//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 pretext = before[Math.floor(Math.random() * before.length)].replace(/{p}/g, message.member.displayName);
- let victim = message.mentions.members.first();
+ 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(" "));
+ if (options.method && options.method.length) {death = options.method;}
+
+ 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.)");}
+
+ if (options.victim && options.victim.length) {
+ let vargs = options.victim.trim().split(/\s+/g);
+ let nvargs = [];
+ let varg; for (varg of vargs) {
+ 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);}
+ }
+ options.victim = nvargs.join(" ").trim();
+ }
+ let victim = options.victim && options.victim.length ? options.victim : message.mentions.members.first().displayName;
let killer = message.member;
+ let pretext = before[Math.floor(Math.random() * before.length)].replace(/{p}/g, victim);
+
let note = await message.channel.send(new Discord.MessageEmbed()
.setDescription(pretext)
.setColor('c375f0')
@@ -82,22 +102,22 @@ module.exports = {
await require('../util/wait')(2500);
let text = reptype.texts[Math.floor(Math.random() * reptype.texts.length)]
- .replace(/{p}/g, victim.displayName) //{p} = victim
- .replace(/{pa}/g, victim.displayName.toLowerCase().endsWith('s') ? `${victim.displayName}'` : `${victim.displayName}'s`) //{pa} = victim but with a formatted apostrophe like "WubzyGD's"
+ .replace(/{p}/g, victim) //{p} = victim
+ .replace(/{pa}/g, victim.toLowerCase().endsWith('s') ? `${victim}'` : `${victim}'s`) //{pa} = victim but with a formatted apostrophe like "WubzyGD's"
.replace(/{c}/g, death) // {c} = death method
.replace(/{w}/g, killer.displayName) // {w} = killer or writer
.replace(/{ds}/g, moment().format("h:mm a")); // {ds} = date small, basically just the time.
// Create and format the kill text
- //TODO message before sending then edit that message i.e. "A name is being written..." then wait 5s
+ let finalEmbed = new Discord.MessageEmbed()
+ .setAuthor(title, message.author.avatarURL())
+ .setDescription(text)
+ .setColor('c375f0')
+ .setFooter("Natsuki")
+ .setTimestamp();
- return note.edit(new Discord.MessageEmbed()
- .setAuthor(title, message.author.avatarURL())
- .setThumbnail(mention.avatarURL({size: 1024}))
- .setDescription(text)
- .setColor('c375f0')
- .setFooter("Natsuki")
- .setTimestamp()
- );
+ if (mention) {finalEmbed.setThumbnail(mention.avatarURL({size: 1024}));}
+
+ return note.edit(finalEmbed);
}
};
\ No newline at end of file
diff --git a/commands/staffrole.js b/commands/staffrole.js
new file mode 100644
index 0000000..7fd0acc
--- /dev/null
+++ b/commands/staffrole.js
@@ -0,0 +1,48 @@
+const Discord = require('discord.js');
+const GuildSettings = require('../models/guild');
+
+module.exports = {
+ name: "staffrole",
+ aliases: ['sr', 'setstaffrole'],
+ help: "Set your server's staff role, which allows users with that role to modify my settings in this server. You must be an admin in the server to change this setting.",
+ async execute(message, msg, args, cmd, prefix, mention, client) {
+ 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});
+
+ 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.`
+ : `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.'
+ );}
+
+ let role = !['c', 'clear', 'n', 'none'].includes(args[0].trim().toLowerCase()) ? message.mentions.roles.size ? message.mentions.roles.first() : message.guild.roles.cache.has(args[0]) ? message.guild.roles.cache.get(args[0]) : null : 'c';
+
+ if (!role) {return message.reply("I couldn't find that role!");}
+ if (role === "c") {
+ tguild.staffrole = '';
+ tguild.save();
+ return message.reply("Got it, only admins can edit my settings in this server.");
+ } else {
+ tguild.staffrole = role.id;
+ tguild.save();
+ let upm = message.reply("Sure thing!");
+ await require('../util/wait')(1750);
+ return upm.edit(new Discord.MessageEmbed()
+ .setAuthor('Staff role updated!', message.author.avatarURL())
+ .setDescription(`<@${tguild.staffrole}> can now edit my settings in this server.`)
+ .addField('Auditing Admin', message.member.displayName, true)
+ .addField('Role-Holders', `${message.guild.members.cache.filter(m => m.roles.cache.has(tguild.staffrole) && !client.users.cache.get(m.id).bot)}+ members have this role`)
+ .setColor('c375f0')
+ .setFooter('Natsuki', client.user.avatarURL())
+ .setTimestamp()
+ );
+ }
+ }
+};
\ No newline at end of file
diff --git a/events/message.js b/events/message.js
index d5e7eb2..71b4e07 100644
--- a/events/message.js
+++ b/events/message.js
@@ -16,7 +16,7 @@ module.exports = async (client, message) => {
var msg = message.content.toLowerCase();
var mention = message.mentions.users.first();
var args = msg.startsWith(prefix)
- ? message.content.slice(prefix.length).trim().split(/\s+/g)
+ ? message.content.slice(prefix.length).trim().split(/\s+/g)
: msg.startsWith('<@!')
? message.content.slice(4 + client.user.id.length).trim().split(/\s+/g)
: message.content.slice(3 + client.user.id.length).trim().split(/\s+/g);
diff --git a/util/tag.d.ts b/util/tag.d.ts
new file mode 100644
index 0000000..4421210
--- /dev/null
+++ b/util/tag.d.ts
@@ -0,0 +1,9 @@
+export declare class Tag {
+ triggers: string[];
+ tagName: string;
+ filterType: TagFilterType;
+ constructor(triggers: string[], tagName: string, filterType: TagFilterType);
+ addTrigger(trigger: string): Tag;
+}
+declare type TagFilterType = "append" | "toggle";
+export {};
diff --git a/util/tag.js b/util/tag.js
new file mode 100644
index 0000000..2a012a9
--- /dev/null
+++ b/util/tag.js
@@ -0,0 +1,21 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Tag = void 0;
+class Tag {
+ constructor(triggers, tagName, filterType) {
+ this.triggers = [];
+ let trigger;
+ for (trigger of triggers) {
+ this.triggers.push(trigger.trim().startsWith("-") ? trigger.trim() : `-${trigger.trim()}`);
+ }
+ this.tagName = tagName;
+ this.filterType = filterType;
+ }
+ ;
+ addTrigger(trigger) {
+ this.triggers.push(trigger.trim().startsWith("-") ? trigger.trim() : `-${trigger.trim()}`);
+ return this;
+ }
+ ;
+}
+exports.Tag = Tag;
diff --git a/util/tagfilter.d.ts b/util/tagfilter.d.ts
new file mode 100644
index 0000000..0da5548
--- /dev/null
+++ b/util/tagfilter.d.ts
@@ -0,0 +1,10 @@
+import { Tag } from "./tag";
+export declare class TagFilter {
+ tags: Tag[];
+ triggers: Map;
+ filterTypes: Map;
+ constructor(tags: Tag[]);
+ test(text: string): object;
+}
+declare type TagFilterType = "append" | "toggle";
+export {};
diff --git a/util/tagfilter.js b/util/tagfilter.js
new file mode 100644
index 0000000..ed77f12
--- /dev/null
+++ b/util/tagfilter.js
@@ -0,0 +1,49 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.TagFilter = void 0;
+class TagFilter {
+ constructor(tags) {
+ this.tags = tags;
+ this.triggers = new Map();
+ this.filterTypes = new Map();
+ let tag;
+ for (tag of this.tags) {
+ let trigger;
+ for (trigger of tag.triggers) {
+ this.triggers.set(trigger, tag.tagName);
+ }
+ if (!this.filterTypes.has(tag.tagName)) {
+ this.filterTypes.set(tag.tagName, tag.filterType);
+ }
+ }
+ }
+ test(text) {
+ var filtered = {};
+ var reading = null;
+ 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();
+ if (!reading) {
+ filtered[`${this.triggers.get(word.trim())}`] = true;
+ }
+ else {
+ filtered[`${this.triggers.get(reading)}`] = '';
+ }
+ }
+ else if (reading) {
+ filtered[`${this.triggers.get(reading)}`] = `${filtered[`${this.triggers.get(reading)}`]} ${word}`;
+ }
+ }
+ let key;
+ for (key of Object.keys(filtered)) {
+ if (typeof filtered[key] == 'string') {
+ filtered[key] = filtered[key].trim();
+ }
+ }
+ return filtered;
+ }
+ ;
+}
+exports.TagFilter = TagFilter;
diff --git a/util/ts/tag.ts b/util/ts/tag.ts
new file mode 100644
index 0000000..86c7ce2
--- /dev/null
+++ b/util/ts/tag.ts
@@ -0,0 +1,25 @@
+export class Tag {
+ triggers: string[] = [];
+ tagName: string;
+ filterType: TagFilterType;
+
+ constructor(triggers: string[], tagName: string, filterType: TagFilterType) {
+ let trigger: string; for (trigger of triggers) {
+ this.triggers.push(
+ trigger.trim().startsWith("-") ? trigger.trim() : `-${trigger.trim()}`
+ );
+ }
+
+ this.tagName = tagName;
+ this.filterType = filterType;
+ };
+
+ public addTrigger(trigger: string): Tag {
+ this.triggers.push(
+ trigger.trim().startsWith("-") ? trigger.trim() : `-${trigger.trim()}`
+ );
+ return this;
+ };
+}
+
+type TagFilterType = "append" | "toggle";
\ No newline at end of file
diff --git a/util/ts/tagfilter.ts b/util/ts/tagfilter.ts
new file mode 100644
index 0000000..fcf04f7
--- /dev/null
+++ b/util/ts/tagfilter.ts
@@ -0,0 +1,45 @@
+import {Tag} from "./tag";
+
+export class TagFilter {
+ tags: Tag[];
+ triggers: Map;
+ filterTypes: Map;
+
+ constructor(tags: Tag[]) {
+ this.tags = tags;
+ this.triggers = new Map();
+ this.filterTypes = new Map();
+ let tag: Tag;
+ for (tag of this.tags) {
+ let trigger: string; for (trigger of tag.triggers) {
+ this.triggers.set(trigger, tag.tagName);
+ }
+ if (!this.filterTypes.has(tag.tagName)) {this.filterTypes.set(tag.tagName, tag.filterType);}
+ }
+ }
+
+ public test(text: string): object {
+ var filtered: object = {};
+ var reading: string = null;
+
+ 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();
+ if (!reading) {filtered[`${this.triggers.get(word.trim())}`] = true;}
+ else {filtered[`${this.triggers.get(reading)}`] = '';}
+ }
+ else if (reading) {
+ filtered[`${this.triggers.get(reading)}`] = `${filtered[`${this.triggers.get(reading)}`]} ${word}`;
+ }
+ }
+
+ let key: string; for (key of Object.keys(filtered)) {
+ if (typeof filtered[key] == 'string') {filtered[key] = filtered[key].trim();}
+ }
+
+ return filtered;
+ };
+}
+
+type TagFilterType = "append" | "toggle";
\ No newline at end of file
diff --git a/util/ts/tsconfig.json b/util/ts/tsconfig.json
new file mode 100644
index 0000000..990d7fa
--- /dev/null
+++ b/util/ts/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "lib": ["es2017", "es6", "dom"],
+ "target": "es2017",
+ "declaration": true,
+ "outDir": "../"
+ },
+ "include": ["**/*"],
+ "exclude": []
+}
\ No newline at end of file