diff --git a/src/commands/moderation/Cases.ts b/src/commands/moderation/Cases.ts index a1fe636..d518274 100644 --- a/src/commands/moderation/Cases.ts +++ b/src/commands/moderation/Cases.ts @@ -27,7 +27,8 @@ export default class Cases { WARN: "⚠️", MUTE: "🔇", KICK: "📤", - BAN: "🔨" + BAN: "🔨", + NOTE: "📝" }; const nothingMsg = [ diff --git a/src/commands/moderation/Note.ts b/src/commands/moderation/Note.ts new file mode 100644 index 0000000..803ac28 --- /dev/null +++ b/src/commands/moderation/Note.ts @@ -0,0 +1,44 @@ +import { SlashCommandSubcommandBuilder, type ChatInputCommandInteraction } from "discord.js"; +import { errorCheck, modEmbed } from "../../utils/embeds/modEmbed"; + +export default class Note { + data: SlashCommandSubcommandBuilder; + constructor() { + this.data = new SlashCommandSubcommandBuilder() + .setName("note") + .setDescription("Add a note on a user.") + .addUserOption(user => + user.setName("user").setDescription("The user that you want to add a note on.").setRequired(true) + ) + .addStringOption(string => + string.setName("note").setDescription("The content of the user note.").setRequired(true) + ) + .addIntegerOption(bool => + bool + .setName("previous_note_id") + .setDescription( + "If provided, will modify the user note with the given case id instead of adding a new one." + ) + ); + } + + async run(interaction: ChatInputCommandInteraction) { + const user = interaction.options.getUser("user")!; + const note = interaction.options.getString("note"); + const previousID = interaction.options.getInteger("previous_note_id") ?? 0; + if ( + await errorCheck( + "ModerateMembers", + { interaction, user, action: "Annotate" }, + { allErrors: true, botError: false, ownerError: true, outsideError: true }, + "Moderate Members" + ) + ) + return; + + await modEmbed( + { interaction, user, action: "Annotated", dm: false, dbAction: "NOTE", previousID: previousID }, + note + ); + } +} diff --git a/src/utils/database/moderation.ts b/src/utils/database/moderation.ts index ce3fc90..73fc0ef 100644 --- a/src/utils/database/moderation.ts +++ b/src/utils/database/moderation.ts @@ -15,7 +15,7 @@ const definition = { } } satisfies TableDefinition; -export type modType = "MUTE" | "WARN" | "KICK" | "BAN"; +export type modType = "MUTE" | "WARN" | "KICK" | "BAN" | "NOTE"; const database = getDatabase(definition); const addQuery = database.query( "INSERT INTO moderation (guild, user, type, moderator, reason, id, timestamp, expiresAt) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);" @@ -29,6 +29,8 @@ const listModQuery = database.query( "SELECT * FROM moderation WHERE guild = $1 AND moderator = $2;" ); const getIdQuery = database.query("SELECT * FROM moderation WHERE guild = $1 AND id = $2;"); +const getLastIdQuery = database.query("SELECT * FROM moderation WHERE guild = $1 ORDER BY id DESC LIMIT 1;"); +const editQuery = database.query("UPDATE moderation SET reason = ?3, expiresAt = ?4 WHERE guild = ?1 AND id = ?2;"); const removeQuery = database.query("DELETE FROM moderation WHERE guild = $1 AND id = $2"); export function addModeration( @@ -39,7 +41,10 @@ export function addModeration( reason = "", expiresAt?: number | null ) { - const id = listGuildQuery.all(guildID).length + 1; + // ISTG we need a DB init file, have an autoincrement on the id and everything is fixed + let id: any = getLastIdQuery.all(guildID); + if (!id.length) return; + id = parseInt(id[0].id) + 1; addQuery.run(guildID, userID, type, moderator, reason, id, Date.now(), expiresAt ?? null); return id; } @@ -57,7 +62,7 @@ export function listUserModeration( export function getModeration(guildID: number | string, userID: number | string, id: string) { const modCase = getIdQuery.all(guildID, id) as TypeOfDefinition[]; - if (modCase[0].user == userID) return modCase; + if (modCase.length && modCase[0].user == userID) return modCase; return []; } @@ -65,6 +70,10 @@ export function listModeratorLog(guildID: number | string, moderator: number | s return listModQuery.all(guildID, moderator) as TypeOfDefinition[]; } +export function editModeration(guildID: number | string, id: string, reason: string, expiresAt?: number | null) { + editQuery.run(guildID, id, reason, expiresAt ?? null); +} + export function removeModeration(guildID: string | number, id: string) { removeQuery.run(guildID, id); } diff --git a/src/utils/embeds/modEmbed.ts b/src/utils/embeds/modEmbed.ts index dc4a582..e2c2421 100644 --- a/src/utils/embeds/modEmbed.ts +++ b/src/utils/embeds/modEmbed.ts @@ -6,7 +6,7 @@ import { } from "discord.js"; import ms from "ms"; import { genColor } from "../colorGen"; -import { addModeration, type modType } from "../database/moderation"; +import { getModeration, addModeration, editModeration, type modType } from "../database/moderation"; import { logChannel } from "../logChannel"; import { errorEmbed } from "./errorEmbed"; @@ -18,6 +18,7 @@ type Options = { dm?: boolean; dbAction?: modType; expiresAt?: number; + previousID?: number; }; type ErrorOptions = { @@ -116,27 +117,45 @@ export async function modEmbed( reason?: string | null, showModerator: boolean = false ) { - const { interaction, user, action, duration, dm, dbAction, expiresAt } = options; + const { interaction, user, action, duration, dm, dbAction, expiresAt, previousID } = options; const guild = interaction.guild!; const name = user.displayName; const generalValues = [`**Moderator**: ${interaction.user.displayName}`]; - let author = `• ${action} ${name}`; + let author = `• ${previousID ? 'Edited a' : ''} ${previousID ? dbAction?.toLowerCase() : action} ${previousID ? 'on' : ''} ${name}`; reason ? generalValues.push(`**Reason**: ${reason}`) : generalValues.push("*No reason provided*"); if (duration) generalValues.push(`**Duration**: ${ms(ms(duration), { long: true })}`); - if (dbAction) - try { - const id = addModeration( - guild.id, - user.id, - dbAction, - guild.members.cache.get(interaction.user.id)?.id!, - reason ?? undefined, - expiresAt ?? undefined + if (previousID) { + let previousCase = getModeration(guild.id, user.id, `${previousID}`); + if (previousCase.length && previousCase[0].user == user.id && previousCase[0].type == dbAction) { + try { + editModeration(guild.id, `${previousID}`, reason ?? '', expiresAt ?? null); + } catch (error) { + console.error(error); + } + author = author.concat(` • #${previousID}`); + } else { + return await errorEmbed( + interaction, + `You can't edit this ${dbAction?.toLowerCase()}.`, + `The ${dbAction?.toLowerCase()} doesn't exist.` ); - author = author.concat(` • #${id}`); - } catch (error) { - console.error(error); } + } else { + if (dbAction) + try { + const id = addModeration( + guild.id, + user.id, + dbAction, + guild.members.cache.get(interaction.user.id)?.id!, + reason ?? undefined, + expiresAt ?? undefined + ); + author = author.concat(` • #${id}`); + } catch (error) { + console.error(error); + } + } const embed = new EmbedBuilder() .setAuthor({ name: author, iconURL: user.displayAvatarURL() })