summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-09-24 02:33:56 -0700
committerFuwn <[email protected]>2025-09-24 02:33:56 -0700
commit401d1220e1a5c2f8c37f2147cc392e0cd783b76f (patch)
tree7082dbb2ac19b8337338c90e78541a44b82fbb37
parentfeat(server): Add new colour roles (diff)
downloadumabotdiscord-401d1220e1a5c2f8c37f2147cc392e0cd783b76f.tar.xz
umabotdiscord-401d1220e1a5c2f8c37f2147cc392e0cd783b76f.zip
feat: Appeal slash command
-rw-r--r--src/discord/commands.ts13
-rw-r--r--src/discord/embeds.ts35
-rw-r--r--src/register.ts2
-rw-r--r--src/server.ts96
4 files changed, 146 insertions, 0 deletions
diff --git a/src/discord/commands.ts b/src/discord/commands.ts
index 652a20a..5f25097 100644
--- a/src/discord/commands.ts
+++ b/src/discord/commands.ts
@@ -71,6 +71,19 @@ export const COMPLAIN_COMMAND: DiscordCommand = {
],
};
+export const APPEAL_COMMAND: DiscordCommand = {
+ name: "appeal",
+ description: "Submit an appeal to the moderators",
+ options: [
+ {
+ type: 3,
+ name: "message",
+ description: "Your appeal message",
+ required: true,
+ },
+ ],
+};
+
export const COLOURS_COMMAND: DiscordCommand = {
name: "colours",
description: "Show the distribution of colour roles in the server",
diff --git a/src/discord/embeds.ts b/src/discord/embeds.ts
index 2eeb800..6873617 100644
--- a/src/discord/embeds.ts
+++ b/src/discord/embeds.ts
@@ -146,3 +146,38 @@ export const createComplaintEmbed = (
},
};
};
+
+export const createAppealEmbed = (
+ appealContent: string,
+ appellant: { username: string; id: string; avatar?: string },
+ timestamp: number,
+ isDM: boolean = true,
+): DiscordEmbed => {
+ return {
+ title: "📋 New Appeal",
+ description: appealContent,
+ color: 0x5865f2,
+ fields: [
+ {
+ name: "Appellant",
+ value: `${appellant.username} (${appellant.id})`,
+ inline: true,
+ },
+ {
+ name: "Timestamp",
+ value: `<t:${Math.floor(timestamp / 1000)}:F>`,
+ inline: true,
+ },
+ ],
+ thumbnail: appellant.avatar
+ ? {
+ url: `https://cdn.discordapp.com/avatars/${appellant.id}/${appellant.avatar}.png?size=256`,
+ }
+ : undefined,
+ footer: {
+ text: isDM
+ ? "Appeal submitted via DM"
+ : "Appeal submitted from server",
+ },
+ };
+};
diff --git a/src/register.ts b/src/register.ts
index 4b5567d..a23c9c8 100644
--- a/src/register.ts
+++ b/src/register.ts
@@ -4,6 +4,7 @@ import {
ROLEPLAY_COMMAND,
TOP_COMMAND,
COMPLAIN_COMMAND,
+ APPEAL_COMMAND,
COLOURS_COMMAND,
ROLEPLAY_SERIOUS_COMMAND,
type DiscordCommand,
@@ -32,6 +33,7 @@ const commands: DiscordCommand[] = [
NSFW_COMMAND,
TOP_COMMAND,
COMPLAIN_COMMAND,
+ APPEAL_COMMAND,
COLOURS_COMMAND,
ROLEPLAY_SERIOUS_COMMAND,
];
diff --git a/src/server.ts b/src/server.ts
index dc7f2cc..990c67c 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -6,6 +6,7 @@ import {
NSFW_COMMAND,
TOP_COMMAND,
COMPLAIN_COMMAND,
+ APPEAL_COMMAND,
COLOURS_COMMAND,
ROLEPLAY_SERIOUS_COMMAND,
} from "./discord/commands.ts";
@@ -20,6 +21,7 @@ import type { Environment, DiscordEmbed } from "./discord/interfaces.ts";
import {
createPostEmbed,
createComplaintEmbed,
+ createAppealEmbed,
createRoleDistributionEmbed,
} from "./discord/embeds.ts";
import { JSONResponse } from "./discord/responses.ts";
@@ -27,6 +29,7 @@ import { verifyDiscordRequest } from "./discord/verification.ts";
const router = AutoRouter();
const COMPLAINT_CHANNEL_ID = "1415868433714778204";
+const APPEAL_CHANNEL_ID = "1420340807931531385";
const SERIOUS_ROLEPLAY_ROLE_ID = "1418311833303122021";
const ROLE_MANAGER_ROLE_ID = "1410993207608873070";
const GUILD_ID = "1406422617724026901";
@@ -86,6 +89,32 @@ const sendComplaintToChannel = async (
}
};
+const sendAppealToChannel = async (
+ environment: Environment,
+ embed: DiscordEmbed,
+): Promise<boolean> => {
+ const url = `https://discord.com/api/v10/channels/${APPEAL_CHANNEL_ID}/messages`;
+
+ try {
+ const response = await fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bot ${environment.DISCORD_TOKEN}`,
+ },
+ body: JSON.stringify({
+ embeds: [embed],
+ }),
+ });
+
+ return response.ok;
+ } catch (error) {
+ console.error("Error sending appeal to channel:", error);
+
+ return false;
+ }
+};
+
const fetchRoleDistribution = async (
environment: Environment,
guildID: string,
@@ -416,6 +445,73 @@ router.post("/", async (request: Request, environment: Environment) => {
}
}
+ case APPEAL_COMMAND.name.toLowerCase(): {
+ try {
+ const appealMessage = interaction.data.options?.[0]
+ ?.value as string;
+
+ if (!appealMessage)
+ return new JSONResponse({
+ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
+ data: {
+ content: "❌ Please provide a message for your appeal.",
+ flags: 64,
+ },
+ });
+
+ const appellant = {
+ username:
+ interaction.member?.user?.username ||
+ interaction.user?.username ||
+ "Unknown",
+ id:
+ interaction.member?.user?.id || interaction.user?.id || "Unknown",
+ avatar:
+ interaction.member?.user?.avatar || interaction.user?.avatar,
+ };
+ const isDM = !interaction.guild_id;
+ const appealEmbed = createAppealEmbed(
+ appealMessage,
+ appellant,
+ Date.now(),
+ isDM,
+ );
+ const success = await sendAppealToChannel(
+ environment,
+ appealEmbed,
+ );
+
+ if (success) {
+ return new JSONResponse({
+ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
+ data: {
+ content: "✅ Your appeal has been submitted successfully! A moderator will follow up with you soon.",
+ flags: 64,
+ },
+ });
+ } else {
+ return new JSONResponse({
+ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
+ data: {
+ content:
+ "❌ Failed to submit your appeal. Please try again later.",
+ flags: 64,
+ },
+ });
+ }
+ } catch (error) {
+ console.error("Error in appeal command:", error);
+
+ return new JSONResponse({
+ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
+ data: {
+ content: "❌ An error occurred while processing your appeal.",
+ flags: 64,
+ },
+ });
+ }
+ }
+
case COLOURS_COMMAND.name.toLowerCase(): {
try {
if (!interaction.guild_id)