feat: report channel message
willow hai@wlo.moe
Wed, 03 Dec 2025 22:29:10 +0000
4 files changed,
211 insertions(+),
3 deletions(-)
M
src/database/schemas.ts
→
src/database/schemas.ts
@@ -23,6 +23,8 @@ bug_channel: String,
commands_channel: String, highlights_channel: String, manager_roles: [String], + report_message: { type: String, default: null }, + report_channel: { type: String, default: null }, }, { timestamps: true }, );@@ -32,6 +34,7 @@ {
user_id: { type: String, required: true }, guild_id: { type: String, required: true, ref: "Guild" }, cat_points: { type: Number, default: 0 }, + has_reported: { type: Boolean, default: false }, }, { timestamps: true }, );
M
src/events/messageCreate.ts
→
src/events/messageCreate.ts
@@ -1,5 +1,5 @@
import config from "@/config"; -import { UserModel } from "@/database/schemas"; +import { GuildModel, UserModel } from "@/database/schemas"; import lily from "@/utils/logging"; import vision from "@google-cloud/vision"; import {@@ -80,19 +80,58 @@
return results; } +async function handleReportChannel(message: Message) { + if (!message.guild) return; + + const guildData = await GuildModel.findOne({ guild_id: message.guild.id }); + if ( + !guildData || + !guildData.report_channel || + message.channel.id !== guildData.report_channel + ) + return; + + let user = await UserModel.findOne({ + user_id: message.author.id, + guild_id: message.guild.id, + }); + + if (!user) { + user = new UserModel({ + user_id: message.author.id, + guild_id: message.guild.id, + }); + } + + if (!user.has_reported) { + user.has_reported = true; + await user.save(); + + if (guildData.report_message) { + await message.reply({ + content: guildData.report_message, + allowedMentions: { users: [message.author.id] }, + }); + } + } +} + export default { event: Events.MessageCreate, async execute(client: Client, message: Message) { + if (message.author.bot) return; + if (message.content.includes("grok is this true")) { if (message.channel.isSendable()) await message.channel.send("yeh"); } + await handleReportChannel(message); + if ( !message.attachments.some((attachment) => attachment.contentType?.startsWith("image/"), ) || - !config.data.catChannel?.channelId || - message.author.bot + !config.data.catChannel?.channelId ) return;
M
src/interactions/commands/config.ts
→
src/interactions/commands/config.ts
@@ -57,6 +57,17 @@ .setName("channel")
.setDescription("channel for highlights") .setRequired(true), ), + ) + .addSubcommand((subcommand) => + subcommand + .setName("report-channel") + .setDescription("set the channel where report are to be sent") + .addChannelOption((option) => + option + .setName("channel") + .setDescription("channel for report") + .setRequired(true), + ), ); async function execute(@@ -105,6 +116,8 @@ } else if (subcommand === "bug-channel") {
await handleBugChannel(interaction, guild); } else if (subcommand === "highlight-channel") { await handleHighlightChannel(interaction, guild); + } else if (subcommand === "report-channel") { + await handleReportChannel(interaction, guild); } else { await interaction.reply({ content: "❌ unknown subcommand.",@@ -234,6 +247,30 @@ await guild.save();
await interaction.reply({ content: `✅ set highlights channel to ${channel}.`, + flags: ["Ephemeral"], + }); +} + +async function handleReportChannel( + interaction: ChatInputCommandInteraction, + guild: GuildType, +) { + const channel = interaction.options.getChannel("channel", true); + + const allowedTypes = [ChannelType.GuildText, ChannelType.GuildAnnouncement]; + if (!allowedTypes.includes(channel.type)) { + await interaction.reply({ + content: "❌ report channel must be a text channel.", + flags: ["Ephemeral"], + }); + return; + } + + guild.report_channel = channel.id; + await guild.save(); + + await interaction.reply({ + content: `✅ set report channel to ${channel}.`, flags: ["Ephemeral"], }); }
A
src/interactions/commands/message.ts
@@ -0,0 +1,129 @@
+import { GuildModel } from "@/database/schemas"; +import lily from "@/utils/logging"; +import { hasManagerPermissions } from "@/utils/permissions"; +import { + ActionRowBuilder, + type ChatInputCommandInteraction, + type Client, + type GuildMember, + LabelBuilder, + ModalBuilder, + type ModalSubmitInteraction, + SlashCommandBuilder, + TextInputBuilder, + TextInputStyle, +} from "discord.js"; + +const logger = lily.child("messageCommand"); + +const MODAL_ID = "message:report_message"; +const INPUT_ID = "message_content"; + +const commandData = new SlashCommandBuilder() + .setName("message") + .setDescription("set the custom report message shown in this server"); + +async function execute( + _client: Client, + interaction: ChatInputCommandInteraction, +) { + if (!interaction.guild || !interaction.member) { + await interaction.reply({ + content: "❌ this command can only be used in a server.", + flags: ["Ephemeral"], + }); + return; + } + + const isManager = await hasManagerPermissions( + interaction.member as GuildMember, + ); + if (!isManager) { + await interaction.reply({ + content: "❌ you do not have permission to manage this server.", + flags: ["Ephemeral"], + }); + return; + } + + try { + const guildData = await GuildModel.findOne({ + guild_id: interaction.guild.id, + }); + + const currentMessage = guildData?.report_message || ""; + + const modal = new ModalBuilder() + .setCustomId(MODAL_ID) + .setTitle("Edit Report Message"); + + const label = new LabelBuilder() + .setLabel("Report Message Content") + .setDescription("This will be the message shown to first time reporters.") + .setTextInputComponent( + new TextInputBuilder() + .setCustomId(INPUT_ID) + .setPlaceholder("meow meow :3") + .setStyle(TextInputStyle.Paragraph) + .setMaxLength(2000) + .setRequired(true) + .setValue(currentMessage), + ); + + modal.addLabelComponents(label); + await interaction.showModal(modal); + } catch (error) { + logger.error("failed to open message modal:", error); + await interaction.reply({ + content: "❌ an error occurred while trying to open the configuration.", + flags: ["Ephemeral"], + }); + } +} + +async function modalExecute( + _client: Client, + interaction: ModalSubmitInteraction, +) { + console.log("modalExecute called"); + if (interaction.customId !== MODAL_ID) return; + + if (!interaction.guild) { + await interaction.reply({ + content: "❌ this command can only be used in a server.", + flags: ["Ephemeral"], + }); + return; + } + + await interaction.deferReply({ flags: ["Ephemeral"] }); + + try { + const newMessage = interaction.fields.getTextInputValue(INPUT_ID); + + await GuildModel.findOneAndUpdate( + { guild_id: interaction.guild.id }, + { report_message: newMessage }, + { upsert: true, new: true }, + ); + + logger.info( + `updated report message for guild ${interaction.guild.id} by ${interaction.user.id}`, + ); + + await interaction.editReply({ + content: "✅ report message updated successfully.", + }); + } catch (error) { + logger.error("failed to save report message:", error); + await interaction.editReply({ + content: "❌ failed to save the report message. please try again later.", + }); + } +} + +export default { + data: commandData, + execute, + modalExecute, +};