add update announcement command to modify existing announcements with enhanced options and permissions
All checks were successful
CI / build (push) Successful in 11s
All checks were successful
CI / build (push) Successful in 11s
This commit is contained in:
222
src/commands/updateAnnouncement.ts
Normal file
222
src/commands/updateAnnouncement.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import { Client, SlashCommandBuilder } from "discord.js";
|
||||
import { registerCommand } from "../lib/commandRegistry";
|
||||
import { CHANNELS, ROLES } from "../config";
|
||||
import { db } from "..";
|
||||
|
||||
export default async function (_client: Client) {
|
||||
registerCommand({
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("update-announcement")
|
||||
.setDescription("Updates an existing announcement")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("message_id")
|
||||
.setDescription(
|
||||
"The Discord message ID of the announcement to update",
|
||||
)
|
||||
.setRequired(true),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("title")
|
||||
.setDescription("New title for the announcement")
|
||||
.setRequired(false),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("content")
|
||||
.setDescription("New content for the announcement")
|
||||
.setRequired(false),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("changes")
|
||||
.setDescription(
|
||||
"New comma-separated list of changes (replaces existing changes)",
|
||||
)
|
||||
.setRequired(false),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("coming_when")
|
||||
.setDescription(
|
||||
"New release date: DD/MM/YYYY or an epoch timestamp",
|
||||
)
|
||||
.setRequired(false),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("link")
|
||||
.setDescription("New link for the announcement")
|
||||
.setRequired(false),
|
||||
) as SlashCommandBuilder,
|
||||
async execute(interaction) {
|
||||
const memberRoles = interaction.member?.roles;
|
||||
let isMaintainer = false;
|
||||
if (memberRoles) {
|
||||
if (typeof memberRoles === "object" && "cache" in memberRoles) {
|
||||
isMaintainer = memberRoles.cache.has(ROLES.MAINTAINERS);
|
||||
} else if (Array.isArray(memberRoles)) {
|
||||
isMaintainer = memberRoles.includes(ROLES.MAINTAINERS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMaintainer) {
|
||||
await interaction.reply({
|
||||
ephemeral: true,
|
||||
content: "You do not have permission to use this command.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const messageId = interaction.options.getString("message_id", true);
|
||||
|
||||
// Look up the original announcement in DB
|
||||
const existing = await db
|
||||
.collection("announcements")
|
||||
.findOne({ messageId });
|
||||
|
||||
if (!existing) {
|
||||
await interaction.reply({
|
||||
ephemeral: true,
|
||||
content:
|
||||
"Announcement not found. Make sure the message ID is correct and was created with `/new-announcement`.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
// Resolve updated values, falling back to existing ones
|
||||
const newTitle =
|
||||
interaction.options.getString("title", false) ?? existing.title;
|
||||
let newContent =
|
||||
interaction.options.getString("content", false) ??
|
||||
existing.content;
|
||||
const changesRaw =
|
||||
interaction.options.getString("changes", false) ??
|
||||
existing.changes;
|
||||
const comingWhenRaw =
|
||||
interaction.options.getString("coming_when", false) ??
|
||||
existing.comingWhen;
|
||||
let link =
|
||||
interaction.options.getString("link", false) ??
|
||||
existing.link ??
|
||||
"https://github.com/Space-Banane/shsf";
|
||||
if (!link || link === "")
|
||||
link = "https://github.com/Space-Banane/shsf";
|
||||
|
||||
// Post Process Content
|
||||
newContent = newContent.replace(/\\n/g, "\n"); // allow users to input \n for newlines
|
||||
|
||||
const fields = [];
|
||||
|
||||
if (changesRaw && changesRaw.trim() !== "") {
|
||||
const changeLines = changesRaw
|
||||
.split(",")
|
||||
.map((c: string) => c.trim())
|
||||
.filter((c: string) => c.length > 0)
|
||||
.map((c: string) => `• ${c}`)
|
||||
.join("\n");
|
||||
fields.push({
|
||||
name: "📋 Changes",
|
||||
value: changeLines,
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (comingWhenRaw && comingWhenRaw.trim() !== "") {
|
||||
let epoch: number | null = null;
|
||||
const trimmed = comingWhenRaw.trim();
|
||||
const ddmmyyyy = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(trimmed);
|
||||
if (ddmmyyyy) {
|
||||
const [, dd, mm, yyyy] = ddmmyyyy;
|
||||
const date = new Date(
|
||||
Number(yyyy),
|
||||
Number(mm) - 1,
|
||||
Number(dd),
|
||||
);
|
||||
if (!isNaN(date.getTime()))
|
||||
epoch = Math.floor(date.getTime() / 1000);
|
||||
} else if (/^\d+$/.test(trimmed)) {
|
||||
epoch = Number(trimmed);
|
||||
}
|
||||
if (epoch !== null) {
|
||||
fields.push({
|
||||
name: "📅 Coming On",
|
||||
value: `<t:${epoch}:F> (<t:${epoch}:R>)`,
|
||||
inline: false,
|
||||
});
|
||||
} else {
|
||||
fields.push({
|
||||
name: "📅 Coming On",
|
||||
value: trimmed,
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fields.push({
|
||||
name: "🔗 More Info",
|
||||
value: `[Click here](${link})`,
|
||||
inline: false,
|
||||
});
|
||||
|
||||
const updatedEmbed = {
|
||||
title: `📢 ${newTitle}`,
|
||||
description: newContent,
|
||||
color: 0x5865f2,
|
||||
fields,
|
||||
footer: {
|
||||
text: `Announced by ${existing.announcedByUsername ?? interaction.user.username} • Last edited by ${interaction.user.username}`,
|
||||
icon_url: interaction.user.displayAvatarURL(),
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Fetch and edit the original message
|
||||
const announcementChannel = interaction.guild?.channels.cache.get(
|
||||
CHANNELS.ANNOUNCEMENTS,
|
||||
);
|
||||
|
||||
if (!announcementChannel?.isTextBased()) {
|
||||
await interaction.editReply({
|
||||
content: "Could not find the announcements channel.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let targetMessage;
|
||||
try {
|
||||
targetMessage =
|
||||
await announcementChannel.messages.fetch(messageId);
|
||||
} catch {
|
||||
await interaction.editReply({
|
||||
content:
|
||||
"Could not fetch the message. Make sure the message ID is correct.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await targetMessage.edit({ embeds: [updatedEmbed] });
|
||||
|
||||
// Update DB
|
||||
await db.collection("announcements").updateOne(
|
||||
{ messageId },
|
||||
{
|
||||
$set: {
|
||||
title: newTitle,
|
||||
content: newContent,
|
||||
changes: changesRaw ?? null,
|
||||
comingWhen: comingWhenRaw ?? null,
|
||||
link,
|
||||
lastEditedBy: interaction.user.id,
|
||||
lastEditedAt: new Date(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await interaction.editReply({ content: "Announcement updated!" });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user