Development (#85)
- updated versioning
- added myself to contributors list :(
- I'm not sure if this works yet. DO NOT PR UNTIL TESTED
- Squashed Bugs
- Fixed User Context Menus
- added flags and fixed user context menus
- fixed server buttons
- added permission
- nothing notable
- fixed lowercase messageCreate logs
diff --git a/.eslintignore b/.eslintignore
index f7770da..0b193f6 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,4 @@
ClicksMigratingProblems/**/*
src/reflex/nsfwjs/**/*
-ecosystem.config.cjs
\ No newline at end of file
+ecosystem.config.cjs
+test.ts
\ No newline at end of file
diff --git a/package.json b/package.json
index 08ca2f2..3591ee0 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"discord-api-types": "0.37.23"
},
"name": "nucleus",
- "version": "0.0.1",
+ "version": "1.1.0",
"description": "Nucleus: The core of your server",
"main": "dist/index.js",
"scripts": {
@@ -61,7 +61,8 @@
"author": "Clicks",
"contributors": [
"Minion3665",
- "PineappleFan"
+ "PineappleFan",
+ "TheCodedProf"
],
"license": "SEE LICENSE IN LICENSE",
"bugs": {
diff --git a/src/commands/mod/about.ts b/src/commands/mod/about.ts
index 9630288..3328d64 100644
--- a/src/commands/mod/about.ts
+++ b/src/commands/mod/about.ts
@@ -1,5 +1,5 @@
import { LoadingEmbed } from "../../utils/defaults.js";
-import type { HistorySchema } from "../../utils/database.js";
+import type { HistorySchema, FlagColors } from "../../utils/database.js";
import Discord, {
CommandInteraction,
GuildMember,
@@ -7,11 +7,13 @@
ActionRowBuilder,
ButtonBuilder,
MessageComponentInteraction,
- ModalSubmitInteraction,
ButtonStyle,
TextInputStyle,
APIMessageComponentEmoji,
- SlashCommandSubcommandBuilder
+ SlashCommandSubcommandBuilder,
+ StringSelectMenuBuilder,
+ StringSelectMenuOptionBuilder,
+ ContextMenuCommandInteraction
} from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
@@ -327,35 +329,41 @@
return timedOut ? 0 : 1;
}
-const callback = async (interaction: CommandInteraction): Promise<unknown> => {
+export const noteMenu = async (
+ member: GuildMember,
+ interaction: CommandInteraction | ContextMenuCommandInteraction
+): Promise<unknown> => {
let m: Message;
- const member = interaction.options.getMember("user") as Discord.GuildMember;
await interaction.reply({
embeds: LoadingEmbed,
ephemeral: true,
fetchReply: true
});
let note;
- let firstLoad = true;
let timedOut = false;
while (!timedOut) {
note = await client.database.notes.read(member.guild.id, member.id);
- if (firstLoad && !note) {
- await showHistory(member, interaction);
- }
- firstLoad = false;
+ const colors: Record<string, Discord.ColorResolvable> = {
+ none: "#424242",
+ red: "#F27878",
+ yellow: "#EDC575",
+ green: "#68D49E",
+ blue: "#6576CC",
+ purple: "#D46899",
+ gray: "#C4C4C4"
+ };
m = (await interaction.editReply({
embeds: [
new EmojiEmbed()
- .setEmoji("MEMBER.JOIN")
+ .setEmoji(`ICONS.FLAGS.${(note?.flag ?? "none").toUpperCase()}`)
.setTitle("Mod notes for " + member.user.username)
- .setDescription(note ? note : "*No note set*")
- .setStatus("Success")
+ .setDescription(note?.note ? note.note : "*No note set*")
+ .setColor(colors[note?.flag ?? "none"]!)
],
components: [
new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
new ButtonBuilder()
- .setLabel(`${note ? "Modify" : "Create"} note`)
+ .setLabel(`${note?.note ? "Modify" : "Create"} note`)
.setStyle(ButtonStyle.Primary)
.setCustomId("modify")
.setEmoji(getEmojiByName("ICONS.EDIT", "id")),
@@ -364,6 +372,49 @@
.setStyle(ButtonStyle.Primary)
.setCustomId("history")
.setEmoji(getEmojiByName("ICONS.HISTORY", "id"))
+ ]),
+ new ActionRowBuilder<Discord.StringSelectMenuBuilder>().addComponents([
+ new StringSelectMenuBuilder()
+ .setCustomId("flag")
+ .setPlaceholder("Select a flag")
+ .addOptions(
+ new StringSelectMenuOptionBuilder()
+ .setLabel("None")
+ .setDefault(!note?.flag)
+ .setValue("none")
+ .setDescription("Clear")
+ .setEmoji(getEmojiByName("ICONS.FLAGS.NONE", "id")),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Red")
+ .setDefault(note?.flag === "red")
+ .setValue("red")
+ .setEmoji(getEmojiByName("ICONS.FLAGS.RED", "id")),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Yellow")
+ .setDefault(note?.flag === "yellow")
+ .setValue("yellow")
+ .setEmoji(getEmojiByName("ICONS.FLAGS.YELLOW", "id")),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Green")
+ .setDefault(note?.flag === "green")
+ .setValue("green")
+ .setEmoji(getEmojiByName("ICONS.FLAGS.GREEN", "id")),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Blue")
+ .setDefault(note?.flag === "blue")
+ .setValue("blue")
+ .setEmoji(getEmojiByName("ICONS.FLAGS.BLUE", "id")),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Purple")
+ .setDefault(note?.flag === "purple")
+ .setValue("purple")
+ .setEmoji(getEmojiByName("ICONS.FLAGS.PURPLE", "id")),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Gray")
+ .setDefault(note?.flag === "gray")
+ .setValue("gray")
+ .setEmoji(getEmojiByName("ICONS.FLAGS.GRAY", "id"))
+ )
])
]
})) as Message;
@@ -383,65 +434,74 @@
timedOut = true;
continue;
}
- if (i.customId === "modify") {
- await i.showModal(
- new Discord.ModalBuilder()
- .setCustomId("modal")
- .setTitle("Editing moderator note")
- .addComponents(
- new ActionRowBuilder<Discord.TextInputBuilder>().addComponents(
- new Discord.TextInputBuilder()
- .setCustomId("note")
- .setLabel("Note")
- .setMaxLength(4000)
- .setRequired(false)
- .setStyle(TextInputStyle.Paragraph)
- .setValue(note ? note : " ")
+ if (i.isButton()) {
+ if (i.customId === "modify") {
+ await i.showModal(
+ new Discord.ModalBuilder()
+ .setCustomId("modal")
+ .setTitle("Editing moderator note")
+ .addComponents(
+ new ActionRowBuilder<Discord.TextInputBuilder>().addComponents(
+ new Discord.TextInputBuilder()
+ .setCustomId("note")
+ .setLabel("Note")
+ .setMaxLength(4000)
+ .setRequired(false)
+ .setStyle(TextInputStyle.Paragraph)
+ .setValue(note?.note ? note.note : " ")
+ )
)
- )
- );
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Mod notes for " + member.user.username)
- .setDescription("Modal opened. If you can't see it, click back and try again.")
- .setStatus("Success")
- .setEmoji("GUILD.TICKET.OPEN")
- ],
- components: [
- new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
- new ButtonBuilder()
- .setLabel("Back")
- .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
- .setStyle(ButtonStyle.Primary)
- .setCustomId("back")
- ])
- ]
- });
- let out;
- try {
- out = await modalInteractionCollector(m, interaction.user);
- } catch (e) {
- timedOut = true;
- continue;
+ );
+ await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("Mod notes for " + member.user.username)
+ .setDescription("Modal opened. If you can't see it, click back and try again.")
+ .setStatus("Success")
+ .setEmoji("GUILD.TICKET.OPEN")
+ ],
+ components: [
+ new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
+ new ButtonBuilder()
+ .setLabel("Back")
+ .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+ .setStyle(ButtonStyle.Primary)
+ .setCustomId("back")
+ ])
+ ]
+ });
+ let out;
+ try {
+ out = await modalInteractionCollector(m, interaction.user);
+ } catch (e) {
+ timedOut = true;
+ continue;
+ }
+ if (out === null || out.isButton()) {
+ continue;
+ } else {
+ let toAdd = out.fields.getTextInputValue("note").trim() || null;
+ if (toAdd === "") toAdd = null;
+ await client.database.notes.create(member.guild.id, member.id, toAdd);
+ }
+ } else if (i.customId === "history") {
+ await i.deferUpdate();
+ if (!(await showHistory(member, interaction))) return;
}
- if (out === null || out.isButton()) {
- continue;
- } else if (out instanceof ModalSubmitInteraction) {
- let toAdd = out.fields.getTextInputValue("note") || null;
- if (toAdd === " ") toAdd = null;
- if (toAdd) toAdd = toAdd.trim();
- await client.database.notes.create(member.guild.id, member.id, toAdd);
- } else {
- continue;
- }
- } else if (i.customId === "history") {
+ } else if (i.isStringSelectMenu()) {
await i.deferUpdate();
- if (!(await showHistory(member, interaction))) return;
+ let flag: string | null = i.values[0]!;
+ if (flag === "none") flag = null;
+ await client.database.notes.flag(member.guild.id, member.id, flag as FlagColors | null);
}
}
};
+const callback = async (interaction: CommandInteraction): Promise<void> => {
+ const member = interaction.options.getMember("user") as Discord.GuildMember;
+ await noteMenu(member, interaction);
+};
+
const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember;
if (!member.permissions.has("ManageMessages")) return "You do not have the *Manage Messages* permission";
diff --git a/src/commands/server/buttons.ts b/src/commands/server/buttons.ts
index f07f3ce..b98e821 100644
--- a/src/commands/server/buttons.ts
+++ b/src/commands/server/buttons.ts
@@ -6,7 +6,6 @@
ChannelSelectMenuBuilder,
ChannelType,
CommandInteraction,
- MessageCreateOptions,
ModalBuilder,
SlashCommandSubcommandBuilder,
StringSelectMenuBuilder,
@@ -98,7 +97,7 @@
.setCustomId("send")
.setLabel("Send")
.setStyle(ButtonStyle.Primary)
- .setDisabled(!data.channel)
+ .setDisabled(!(data.channel && (data.title ?? data.description ?? data.buttons.length)))
);
const colorSelect = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
@@ -137,7 +136,7 @@
new StringSelectMenuBuilder()
.setCustomId("button")
.setPlaceholder("Select buttons to add")
- .setMinValues(1)
+ .setMinValues(0)
.setMaxValues(3)
.addOptions(
new StringSelectMenuOptionBuilder()
@@ -175,7 +174,7 @@
.setTitle(data.title ?? "No title set")
.setDescription(data.description ?? "*No description set*")
.setColor(data.color)
- .setFooter({ text: `Click the button below to edit the embed | The embed will be sent in ${channelName}` });
+ .setFooter({ text: `The embed will be sent in ${channelName} | Click the button below to edit the embed` });
await interaction.editReply({
embeds: [embed],
@@ -266,9 +265,10 @@
case "send": {
await i.deferUpdate();
const channel = interaction.guild!.channels.cache.get(data.channel!) as Discord.TextChannel;
- const messageData: MessageCreateOptions = {};
+ let components: ActionRowBuilder<ButtonBuilder>[] = [];
+ let embeds: EmojiEmbed[] = [];
for (const button of data.buttons) {
- messageData.components = [
+ components = [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId(button)
@@ -277,14 +277,14 @@
)
];
}
- if (data.title || data.description || data.color) {
+ if (data.title || data.description) {
const e = new EmojiEmbed();
if (data.title) e.setTitle(data.title);
if (data.description) e.setDescription(data.description);
if (data.color) e.setColor(data.color);
- messageData.embeds = [e];
+ embeds = [e];
}
- await channel.send(messageData);
+ await channel.send({ embeds, components });
break;
}
}
diff --git a/src/commands/settings/automod.ts b/src/commands/settings/automod.ts
index 5b4198f..de8e740 100644
--- a/src/commands/settings/automod.ts
+++ b/src/commands/settings/automod.ts
@@ -757,8 +757,9 @@
.addComponents(
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
+ .setLabel("Amount")
.setCustomId("mass")
- .setPlaceholder("Amount")
+ .setPlaceholder(current.mass === 5 ? "Amount" : current.mass.toString())
.setMinLength(1)
.setMaxLength(3)
.setStyle(TextInputStyle.Short)
diff --git a/src/config/emojis.ts b/src/config/emojis.ts
index b7ed91a..b04c569 100644
--- a/src/config/emojis.ts
+++ b/src/config/emojis.ts
@@ -51,6 +51,7 @@
CATEGORY: "1064943289708597348"
},
FLAGS: {
+ NONE: "1099782406417940520",
RED: "1082719687219101800",
YELLOW: "1082719684060794890",
GREEN: "1082719681326108763",
diff --git a/src/context/messages/purgeto.ts b/src/context/messages/purgeto.ts
index a75a281..0b7723e 100644
--- a/src/context/messages/purgeto.ts
+++ b/src/context/messages/purgeto.ts
@@ -9,12 +9,15 @@
GuildMember,
GuildTextBasedChannel,
Message,
- MessageContextMenuCommandInteraction
+ MessageContextMenuCommandInteraction,
+ PermissionFlagsBits
} from "discord.js";
import client from "../../utils/client.js";
import { messageException } from "../../utils/createTemporaryStorage.js";
-const command = new ContextMenuCommandBuilder().setName("Purge up to here");
+const command = new ContextMenuCommandBuilder()
+ .setName("Purge up to Here")
+ .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages);
async function waitForButton(m: Discord.Message, member: Discord.GuildMember): Promise<boolean> {
let component;
@@ -38,9 +41,6 @@
const channel = interaction.channel;
if (!channel) return;
await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
- // Option for "include this message"?
- // Option for "Only selected user"?
-
const history: Discord.Collection<string, Discord.Message> = await channel.messages.fetch({ limit: 100 });
if (Date.now() - targetMessage.createdTimestamp > 2 * 7 * 24 * 60 * 60 * 1000) {
const m = await interaction.editReply({
diff --git a/src/context/users/flaguser.ts b/src/context/users/flaguser.ts
new file mode 100644
index 0000000..e0578a0
--- /dev/null
+++ b/src/context/users/flaguser.ts
@@ -0,0 +1,24 @@
+import {
+ ContextMenuCommandBuilder,
+ GuildMember,
+ PermissionFlagsBits,
+ UserContextMenuCommandInteraction
+} from "discord.js";
+import { noteMenu } from "../../commands/mod/about.js";
+
+const command = new ContextMenuCommandBuilder()
+ .setName("Flag User")
+ .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages);
+
+const callback = async (interaction: UserContextMenuCommandInteraction) => {
+ const guild = interaction.guild!;
+ let member = interaction.targetMember as GuildMember | null;
+ if (!member) member = await guild.members.fetch(interaction.targetId);
+ await noteMenu(member, interaction);
+};
+
+const check = async (_interaction: UserContextMenuCommandInteraction) => {
+ return true;
+};
+
+export { command, callback, check };
diff --git a/src/context/users/userinfo.ts b/src/context/users/userinfo.ts
index 9c7433f..a02766f 100644
--- a/src/context/users/userinfo.ts
+++ b/src/context/users/userinfo.ts
@@ -5,12 +5,14 @@
const callback = async (interaction: UserContextMenuCommandInteraction) => {
const guild = interaction.guild!;
- let member = interaction.targetMember;
+ let member = interaction.targetMember as GuildMember | null;
if (!member) member = await guild.members.fetch(interaction.targetId);
await userAbout(guild, member as GuildMember, interaction);
};
-const check = async (_interaction: UserContextMenuCommandInteraction) => {
+const check = async (interaction: UserContextMenuCommandInteraction) => {
+ if (!interaction.inGuild()) return "You must be in a server to use this command.";
+
return true;
};
diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts
index b79c2da..e396c03 100644
--- a/src/events/channelDelete.ts
+++ b/src/events/channelDelete.ts
@@ -8,12 +8,50 @@
ThreadChannel,
VoiceChannel
} from "discord.js";
-import type { NucleusClient } from "../utils/client.js";
+import _client, { NucleusClient } from "../utils/client.js";
import getEmojiByName from "../utils/getEmojiByName.js";
export const event = "channelDelete";
+// function getPropFromObject(splitProp: string[], object: Record<string, unknown>) {
+// if (splitProp.length === 0) return null
+// if (splitProp.length === 1) {
+// return object[splitProp[0]!]
+// }
+// const property: string = splitProp[0]!
+// if (! Object.keys(object).includes(property)) return null;
+// splitProp = splitProp.splice(1)
+// return getPropFromObject(splitProp, object[property] as Record<string, unknown>)
+// }
+
+// async function deleteFromGuildConfig(channel: GuildBasedChannel) {
+// const guildConfig = await client.database.guilds.read(channel.guild.id);
+// const lists = [
+// "filters.wordFilter.allowed.channels",
+// "filters.invite.allowed.channels",
+// "filters.pings.allowed.channels",
+// "filters.clean.allowed.channels",
+// "filters.autoPublish.allowed.channels"
+// ]
+// const single = [
+// "welcome.channel",
+// "logging.logs.channel",
+// "logging.staff.channel",
+// "logging.attachments.channel",
+// "tickets.category"
+// ]
+// console.log(guildConfig, lists, single)
+// // for (const list of lists) {
+// // const index = guildConfig[list].indexOf(channel.id);
+// // if (index !== -1) guildConfig[list].splice(index, 1);
+// // }
+// };
+
export async function callback(client: NucleusClient, channel: GuildBasedChannel) {
+ // In future, please avoid using client from the outer scope. If you import client separately this
+ // parameter should shadow it.
+
+ // await deleteFromGuildConfig(channel)
const { getAuditLog, log, isLogging, NucleusColors, entry, renderDelta, renderUser } = client.logger;
if (!(await isLogging(channel.guild.id, "channelUpdate"))) return;
const auditLog = (await getAuditLog(channel.guild, AuditLogEvent.ChannelDelete)).filter(
diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts
index cc69bf0..e1aa359 100644
--- a/src/events/messageCreate.ts
+++ b/src/events/messageCreate.ts
@@ -5,11 +5,19 @@
import getEmojiByName from "../utils/getEmojiByName.js";
import client from "../utils/client.js";
import { callback as statsChannelUpdate } from "../reflex/statsChannelUpdate.js";
-import { ChannelType, Message, ThreadChannel } from "discord.js";
+import { ChannelType, GuildMember, Message, ThreadChannel } from "discord.js";
import singleNotify from "../utils/singleNotify.js";
export const event = "messageCreate";
+function checkUserForExceptions(user: GuildMember, exceptions: { roles: string[]; users: string[] }) {
+ if (exceptions.users.includes(user.id)) return true;
+ for (const role of user.roles.cache.values()) {
+ if (exceptions.roles.includes(role.id)) return true;
+ }
+ return false;
+}
+
export async function callback(_client: NucleusClient, message: Message) {
if (!message.guild) return;
const config = await client.memory.readGuildInfo(message.guild.id);
@@ -42,17 +50,14 @@
const { log, isLogging, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger;
const fileNames = await logAttachment(message);
-
- const content = message.content.toLowerCase() || "";
+ const lowerCaseContent = message.content.toLowerCase() || "";
if (config.filters.clean.channels.includes(message.channel.id)) {
- const memberRoles = message.member!.roles.cache.map((role) => role.id);
- const roleAllow = config.filters.clean.allowed.roles.some((role) => memberRoles.includes(role));
- const userAllow = config.filters.clean.allowed.users.includes(message.author.id);
- if (!roleAllow && !userAllow) return await message.delete();
+ if (!checkUserForExceptions(message.member!, config.filters.clean.allowed)) return await message.delete();
}
const filter = getEmojiByName("ICONS.FILTER");
let attachmentJump = "";
+ console.log(config.logging.attachments.saved);
if (config.logging.attachments.saved[message.channel.id + message.id]) {
attachmentJump = ` [[View attachments]](${config.logging.attachments.saved[message.channel.id + message.id]})`;
}
@@ -72,32 +77,38 @@
};
if (config.filters.invite.enabled) {
- if (!config.filters.invite.allowed.channels.includes(message.channel.id)) {
- if (/(?:https?:\/\/)?discord(?:app)?\.(?:com\/invite|gg)\/[a-zA-Z0-9]+\/?/.test(content)) {
- messageException(message.guild.id, message.channel.id, message.id);
- await message.delete();
- const data = {
- meta: {
- type: "messageDelete",
- displayName: "Message Deleted",
- calculateType: "autoModeratorDeleted",
- color: NucleusColors.red,
- emoji: "MESSAGE.DELETE",
- timestamp: Date.now()
- },
- separate: {
- start:
- filter +
- " Contained invite\n\n" +
- (content ? `**Message:**\n\`\`\`${content}\`\`\`` : "**Message:** *Message had no content*")
- },
- list: list,
- hidden: {
- guild: message.channel.guild.id
- }
- };
- return log(data);
- }
+ if (
+ !(
+ config.filters.invite.allowed.channels.includes(message.channel.id) ||
+ checkUserForExceptions(message.member!, config.filters.invite.allowed)
+ ) &&
+ /(?:https?:\/\/)?discord(?:app)?\.(?:com\/invite|gg)\/[a-zA-Z0-9]+\/?/.test(lowerCaseContent)
+ ) {
+ messageException(message.guild.id, message.channel.id, message.id);
+ await message.delete();
+ const data = {
+ meta: {
+ type: "messageDelete",
+ displayName: "Message Deleted",
+ calculateType: "autoModeratorDeleted",
+ color: NucleusColors.red,
+ emoji: "MESSAGE.DELETE",
+ timestamp: Date.now()
+ },
+ separate: {
+ start:
+ filter +
+ " Contained invite\n\n" +
+ (lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
+ : "**Message:** *Message had no content*")
+ },
+ list: list,
+ hidden: {
+ guild: message.channel.guild.id
+ }
+ };
+ return log(data);
}
}
@@ -131,8 +142,8 @@
start:
filter +
" Image detected as NSFW\n\n" +
- (content
- ? `**Message:**\n\`\`\`${content}\`\`\``
+ (lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
: "**Message:** *Message had no content*")
},
list: list,
@@ -149,7 +160,11 @@
config.filters.wordFilter.words.loose,
config.filters.wordFilter.words.strict
);
- if (check !== null) {
+ if (
+ check !== null &&
+ (!checkUserForExceptions(message.member!, config.filters.wordFilter.allowed) ||
+ !config.filters.wordFilter.allowed.channels.includes(message.channel.id))
+ ) {
messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
@@ -165,8 +180,8 @@
start:
filter +
" Image contained filtered word\n\n" +
- (content
- ? `**Message:**\n\`\`\`${content}\`\`\``
+ (lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
: "**Message:** *Message had no content*")
},
list: list,
@@ -195,8 +210,8 @@
start:
filter +
" Image was too small\n\n" +
- (content
- ? `**Message:**\n\`\`\`${content}\`\`\``
+ (lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
: "**Message:** *Message had no content*")
},
list: list,
@@ -225,7 +240,9 @@
start:
filter +
" File detected as malware\n\n" +
- (content ? `**Message:**\n\`\`\`${content}\`\`\`` : "**Message:** *Message had no content*")
+ (lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
+ : "**Message:** *Message had no content*")
},
list: list,
hidden: {
@@ -254,7 +271,9 @@
start:
filter +
` Link filtered as ${linkDetectionTypes[0]?.toLowerCase()}\n\n` +
- (content ? `**Message:**\n\`\`\`${content}\`\`\`` : "**Message:** *Message had no content*")
+ (lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
+ : "**Message:** *Message had no content*")
},
list: list,
hidden: {
@@ -264,9 +283,15 @@
return log(data);
}
- if (config.filters.wordFilter.enabled) {
+ if (
+ config.filters.wordFilter.enabled &&
+ !(
+ checkUserForExceptions(message.member!, config.filters.wordFilter.allowed) ||
+ config.filters.wordFilter.allowed.channels.includes(message.channel.id)
+ )
+ ) {
const check = TestString(
- content,
+ lowerCaseContent,
config.filters.wordFilter.words.loose,
config.filters.wordFilter.words.strict
);
@@ -286,7 +311,9 @@
start:
filter +
" Message contained filtered word\n\n" +
- (content ? `**Message:**\n\`\`\`${content}\`\`\`` : "**Message:** *Message had no content*")
+ (lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
+ : "**Message:** *Message had no content*")
},
list: list,
hidden: {
@@ -297,7 +324,11 @@
}
}
- if (config.filters.pings.everyone && message.mentions.everyone) {
+ if (
+ config.filters.pings.everyone &&
+ message.mentions.everyone &&
+ !checkUserForExceptions(message.member!, config.filters.pings.allowed)
+ ) {
if (!(await isLogging(message.guild.id, "messageMassPing"))) return;
const data = {
meta: {
@@ -309,7 +340,9 @@
timestamp: Date.now()
},
separate: {
- start: content ? `**Message:**\n\`\`\`${content}\`\`\`` : "**Message:** *Message had no content*"
+ start: lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
+ : "**Message:** *Message had no content*"
},
list: list,
hidden: {
@@ -318,7 +351,7 @@
};
return log(data);
}
- if (config.filters.pings.roles) {
+ if (config.filters.pings.roles && !checkUserForExceptions(message.member!, config.filters.pings.allowed)) {
for (const roleId in message.mentions.roles) {
if (!config.filters.pings.allowed.roles.includes(roleId)) {
messageException(message.guild.id, message.channel.id, message.id);
@@ -334,8 +367,8 @@
timestamp: Date.now()
},
separate: {
- start: content
- ? `**Message:**\n\`\`\`${content}\`\`\``
+ start: lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
: "**Message:** *Message had no content*"
},
list: list,
@@ -347,7 +380,11 @@
}
}
}
- if (message.mentions.users.size >= config.filters.pings.mass && config.filters.pings.mass) {
+ if (
+ message.mentions.users.size >= config.filters.pings.mass &&
+ config.filters.pings.mass &&
+ !checkUserForExceptions(message.member!, config.filters.pings.allowed)
+ ) {
messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
if (!(await isLogging(message.guild.id, "messageMassPing"))) return;
@@ -361,7 +398,9 @@
timestamp: Date.now()
},
separate: {
- start: content ? `**Message:**\n\`\`\`${content}\`\`\`` : "**Message:** *Message had no content*"
+ start: lowerCaseContent
+ ? `**Message:**\n\`\`\`${message.content}\`\`\``
+ : "**Message:** *Message had no content*"
},
list: list,
hidden: {
diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts
index d76617c..5889d54 100644
--- a/src/utils/commandRegistration/register.ts
+++ b/src/utils/commandRegistration/register.ts
@@ -156,7 +156,13 @@
context.command.setType(ApplicationCommandType.User);
commands.push(context.command);
- client.commands["contextCommands/user/" + context.command.name] = context;
+ client.commands["contextCommands/user/" + context.command.name] = [
+ context,
+ {
+ name: context.name ?? context.command.name,
+ description: context.description ?? context.command.description
+ }
+ ];
console.log(
`${last.replace("└", " ").replace("├", "│")} └─ ${colors.green}Loaded ${
diff --git a/src/utils/database.ts b/src/utils/database.ts
index b42b8b2..77ff875 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -696,15 +696,25 @@
this.modNotes = database.collection<ModNoteSchema>("modNotes");
}
+ async flag(guild: string, user: string, flag: FlagColors | null) {
+ const modNote = await this.modNotes.findOne({ guild: guild, user: user });
+ modNote
+ ? await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { flag: flag } }, collectionOptions)
+ : await this.modNotes.insertOne({ guild: guild, user: user, note: null, flag: flag }, collectionOptions);
+ }
+
async create(guild: string, user: string, note: string | null) {
// console.log("ModNotes create");
- await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, { upsert: true });
+ const modNote = await this.modNotes.findOne({ guild: guild, user: user });
+ modNote
+ ? await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, collectionOptions)
+ : await this.modNotes.insertOne({ guild: guild, user: user, note: note, flag: null }, collectionOptions);
}
async read(guild: string, user: string) {
// console.log("ModNotes read");
const entry = await this.modNotes.findOne({ guild: guild, user: user });
- return entry?.note ?? null;
+ return entry ?? null;
}
async delete(guild: string) {
@@ -1033,10 +1043,13 @@
amount: string | null;
}
+export type FlagColors = "red" | "yellow" | "green" | "blue" | "purple" | "gray";
+
export interface ModNoteSchema {
guild: string;
user: string;
note: string | null;
+ flag: FlagColors | null;
}
export interface PremiumSchema {