From 77a2e6cbb80b5a69b8a609523343759d655e2156 Mon Sep 17 00:00:00 2001 From: Jacob Date: Wed, 7 Aug 2024 08:03:31 +0800 Subject: [PATCH] Sticky Messages --- src/commands/sticky.ts | 116 +++++++++++++++++++++++++++++++++ src/handlers/MessageHandler.ts | 7 ++ 2 files changed, 123 insertions(+) create mode 100644 src/commands/sticky.ts diff --git a/src/commands/sticky.ts b/src/commands/sticky.ts new file mode 100644 index 0000000..769b139 --- /dev/null +++ b/src/commands/sticky.ts @@ -0,0 +1,116 @@ +import { + ChatInputCommandInteraction, + PermissionFlagsBits, + SlashCommandBuilder, + EmbedBuilder, + ChannelType +} from 'discord.js'; +import { model, Schema } from 'mongoose'; + +export const data = new SlashCommandBuilder() + .setName('sticky') + .setDescription('Sticky Messages') + .addSubcommand((subcommand) => + subcommand + .setName('set') + .setDescription('Set a sticky message for a channel') + .addStringOption((option) => option.setName('message').setDescription('Message').setRequired(true)) + ) + .addSubcommand((subcommand) => + subcommand + .setName('get') + .setDescription('Get the sticky message for a channel') + .addChannelOption((option) => + option.setName('channel').setDescription('Channel').addChannelTypes(ChannelType.GuildText).setRequired(false) + ) + ) + .addSubcommand((subcommand) => + subcommand + .setName('delete') + .setDescription('Delete the sticky message for a channel') + .addChannelOption((option) => + option.setName('channel').setDescription('Channel').addChannelTypes(ChannelType.GuildText).setRequired(false) + ) + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) + .setDMPermission(false); + +const userSchema = new Schema({ id: String, staff: Boolean, bot: Boolean }); +const stickySchema = new Schema({ content: String, message: String, channel: String, user: userSchema }); +const stickyModel = model('Sticky', stickySchema); + +export async function getStickyMessage( + channelId: string +): Promise<{ content: string; message: string; channel: string } | null> { + const stickyMessage = await stickyModel.findOne({ channel: channelId }); + if (!stickyMessage || !stickyMessage.content || !stickyMessage.message || !stickyMessage.channel) return null; + return { content: stickyMessage.content, message: stickyMessage.message, channel: stickyMessage.channel }; +} + +export async function updateStickyMessage(channelId: string, messageId: string): Promise { + await stickyModel.findOneAndUpdate({ channel: channelId }, { message: messageId }); +} + +async function deleteStickyMessage(channelId: string): Promise { + await stickyModel.findOneAndDelete({ channel: channelId }); +} + +export async function execute(interaction: ChatInputCommandInteraction): Promise { + try { + if (!interaction.channel) return; + const subCommand = interaction.options.getSubcommand(); + switch (subCommand) { + case 'set': { + const message = interaction.options.getString('message', true); + const sentMessage = await interaction.channel.send({ content: message }); + new stickyModel({ + content: message, + message: sentMessage.id, + channel: interaction.channel.id, + user: { id: interaction.user.id, staff: true, bot: interaction.user.bot } + }).save(); + await interaction.reply({ content: 'Sticky message has been set', ephemeral: true }); + break; + } + case 'get': { + const channel = interaction.options.getChannel('channel') ?? interaction.channel; + if (!channel || channel.type !== ChannelType.GuildText) return; + const stickyMessage = await getStickyMessage(channel.id); + if (!stickyMessage) { + await interaction.reply({ content: 'No sticky message found for this channel', ephemeral: true }); + return; + } + await interaction.reply({ content: stickyMessage.content, ephemeral: true }); + break; + } + case 'delete': { + const channel = await interaction.guild?.channels.fetch( + interaction.options.getChannel('channel')?.id ?? interaction.channel.id + ); + if (!channel || channel.type !== ChannelType.GuildText) return; + const stickyMessage = await getStickyMessage(channel.id); + if (null === stickyMessage) { + await interaction.reply({ content: 'No sticky message found for this channel', ephemeral: true }); + return; + } + channel.messages.fetch(stickyMessage.message).then((message) => message.delete()); + await deleteStickyMessage(channel.id); + await interaction.reply({ content: 'Sticky message has been deleted', ephemeral: true }); + break; + } + default: { + const embed = new EmbedBuilder() + .setTitle('Invalid subcommand') + .setDescription('Please provide a valid subcommand'); + await interaction.reply({ embeds: [embed] }); + } + } + } catch (error) { + console.log(error); + if (interaction.replied || interaction.deferred) { + await interaction.followUp({ content: 'Something went wrong. Please try again later.', ephemeral: true }); + return; + } + await interaction.reply({ content: 'Something went wrong. Please try again later.', ephemeral: true }); + } +} diff --git a/src/handlers/MessageHandler.ts b/src/handlers/MessageHandler.ts index a0f42bd..d68fd87 100644 --- a/src/handlers/MessageHandler.ts +++ b/src/handlers/MessageHandler.ts @@ -1,5 +1,6 @@ import { ChannelType, GuildMemberRoleManager, Message, TextChannel, Webhook, WebhookType } from 'discord.js'; import { DiscordInviteRegex, HypixelAPIKeyRegex, IPAddressPattern, URLRegex } from '../utils/regex'; +import { getStickyMessage, updateStickyMessage } from '../commands/sticky'; import { getAllowedDomains, getAntiLinkState } from '../utils/mongo'; import { autoModBypassRole } from '../../config.json'; import DiscordManager from '../DiscordManager'; @@ -20,6 +21,12 @@ class MessageHandler { async onMessage(message: Message) { if (!message.member) return; + const sticky = await getStickyMessage(message.channel.id); + if (null !== sticky && message.author !== message.client.user) { + message.channel.messages.fetch(sticky.message).then((message) => message.delete()); + const newMsg = await message.channel.send({ content: sticky.content }); + updateStickyMessage(message.channel.id, newMsg.id); + } const memberRoles = (message.member.roles as GuildMemberRoleManager).cache.map((role) => role.id); if (memberRoles.includes(autoModBypassRole)) return; this.updateAllowedDomains();