forked from syntaxbullet/AuroraBot-discord
refactor: initial moves
This commit is contained in:
79
bot/modules/feedback/feedback.interaction.ts
Normal file
79
bot/modules/feedback/feedback.interaction.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import type { Interaction } from "discord.js";
|
||||
import { TextChannel, MessageFlags } from "discord.js";
|
||||
import { config } from "@/lib/config";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { buildFeedbackMessage, getFeedbackModal } from "./feedback.view";
|
||||
import { FEEDBACK_CUSTOM_IDS, type FeedbackType, type FeedbackData } from "./feedback.types";
|
||||
import { UserError } from "@/lib/errors";
|
||||
|
||||
export const handleFeedbackInteraction = async (interaction: Interaction) => {
|
||||
// Handle select menu for choosing feedback type
|
||||
if (interaction.isStringSelectMenu() && interaction.customId === "feedback_select_type") {
|
||||
const feedbackType = interaction.values[0] as FeedbackType;
|
||||
|
||||
if (!feedbackType) {
|
||||
throw new UserError("Invalid feedback type selected.");
|
||||
}
|
||||
|
||||
const modal = getFeedbackModal(feedbackType);
|
||||
await interaction.showModal(modal);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle modal submission
|
||||
if (interaction.isModalSubmit() && interaction.customId.startsWith(FEEDBACK_CUSTOM_IDS.MODAL)) {
|
||||
// Extract feedback type from customId (format: feedback_modal_FEATURE_REQUEST)
|
||||
const parts = interaction.customId.split("_");
|
||||
const feedbackType = parts.slice(2).join("_") as FeedbackType;
|
||||
|
||||
console.log(`Processing feedback modal. CustomId: ${interaction.customId}, Extracted type: ${feedbackType}`);
|
||||
|
||||
if (!feedbackType || !["FEATURE_REQUEST", "BUG_REPORT", "GENERAL"].includes(feedbackType)) {
|
||||
console.error(`Invalid feedback type extracted: ${feedbackType} from customId: ${interaction.customId}`);
|
||||
throw new UserError("An error occurred processing your feedback. Please try again.");
|
||||
}
|
||||
|
||||
if (!config.feedbackChannelId) {
|
||||
throw new UserError("Feedback channel is not configured. Please contact an administrator.");
|
||||
}
|
||||
|
||||
// Parse modal inputs
|
||||
const title = interaction.fields.getTextInputValue(FEEDBACK_CUSTOM_IDS.TITLE_FIELD);
|
||||
const description = interaction.fields.getTextInputValue(FEEDBACK_CUSTOM_IDS.DESCRIPTION_FIELD);
|
||||
|
||||
// Build feedback data
|
||||
const feedbackData: FeedbackData = {
|
||||
type: feedbackType,
|
||||
title,
|
||||
description,
|
||||
userId: interaction.user.id,
|
||||
username: interaction.user.username,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
// Get feedback channel
|
||||
const channel = await AuroraClient.channels.fetch(config.feedbackChannelId).catch(() => null) as TextChannel | null;
|
||||
|
||||
if (!channel) {
|
||||
throw new UserError("Feedback channel not found. Please contact an administrator.");
|
||||
}
|
||||
|
||||
// Build and send beautiful message
|
||||
const containers = buildFeedbackMessage(feedbackData);
|
||||
|
||||
const feedbackMessage = await channel.send({
|
||||
components: containers as any,
|
||||
flags: MessageFlags.IsComponentsV2
|
||||
});
|
||||
|
||||
// Add reaction votes
|
||||
await feedbackMessage.react("👍");
|
||||
await feedbackMessage.react("👎");
|
||||
|
||||
// Confirm to user
|
||||
await interaction.reply({
|
||||
content: "✨ **Feedback Submitted**\nYour feedback has been submitted successfully! Thank you for helping improve Aurora.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
};
|
||||
23
bot/modules/feedback/feedback.types.ts
Normal file
23
bot/modules/feedback/feedback.types.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export type FeedbackType = "FEATURE_REQUEST" | "BUG_REPORT" | "GENERAL";
|
||||
|
||||
export interface FeedbackData {
|
||||
type: FeedbackType;
|
||||
title: string;
|
||||
description: string;
|
||||
userId: string;
|
||||
username: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export const FEEDBACK_TYPE_LABELS: Record<FeedbackType, string> = {
|
||||
FEATURE_REQUEST: "💡 Feature Request",
|
||||
BUG_REPORT: "🐛 Bug Report",
|
||||
GENERAL: "💬 General Feedback"
|
||||
};
|
||||
|
||||
export const FEEDBACK_CUSTOM_IDS = {
|
||||
MODAL: "feedback_modal",
|
||||
TYPE_FIELD: "feedback_type",
|
||||
TITLE_FIELD: "feedback_title",
|
||||
DESCRIPTION_FIELD: "feedback_description"
|
||||
} as const;
|
||||
123
bot/modules/feedback/feedback.view.ts
Normal file
123
bot/modules/feedback/feedback.view.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ActionRowBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
ActionRowBuilder as MessageActionRowBuilder,
|
||||
ContainerBuilder,
|
||||
TextDisplayBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle
|
||||
} from "discord.js";
|
||||
import { FEEDBACK_TYPE_LABELS, FEEDBACK_CUSTOM_IDS, type FeedbackData, type FeedbackType } from "./feedback.types";
|
||||
|
||||
export function getFeedbackTypeMenu() {
|
||||
const select = new StringSelectMenuBuilder()
|
||||
.setCustomId("feedback_select_type")
|
||||
.setPlaceholder("Choose feedback type")
|
||||
.addOptions([
|
||||
{
|
||||
label: "💡 Feature Request",
|
||||
description: "Suggest a new feature or improvement",
|
||||
value: "FEATURE_REQUEST"
|
||||
},
|
||||
{
|
||||
label: "🐛 Bug Report",
|
||||
description: "Report a bug or issue",
|
||||
value: "BUG_REPORT"
|
||||
},
|
||||
{
|
||||
label: "💬 General Feedback",
|
||||
description: "Share your thoughts or suggestions",
|
||||
value: "GENERAL"
|
||||
}
|
||||
]);
|
||||
|
||||
const row = new MessageActionRowBuilder<StringSelectMenuBuilder>().addComponents(select);
|
||||
return { components: [row] };
|
||||
}
|
||||
|
||||
export function getFeedbackModal(feedbackType: FeedbackType) {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId(`${FEEDBACK_CUSTOM_IDS.MODAL}_${feedbackType}`)
|
||||
.setTitle(FEEDBACK_TYPE_LABELS[feedbackType]);
|
||||
|
||||
// Title Input
|
||||
const titleInput = new TextInputBuilder()
|
||||
.setCustomId(FEEDBACK_CUSTOM_IDS.TITLE_FIELD)
|
||||
.setLabel("Title")
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setPlaceholder("Brief summary of your feedback")
|
||||
.setRequired(true)
|
||||
.setMaxLength(100);
|
||||
|
||||
const titleRow = new ActionRowBuilder<TextInputBuilder>().addComponents(titleInput);
|
||||
|
||||
// Description Input
|
||||
const descriptionInput = new TextInputBuilder()
|
||||
.setCustomId(FEEDBACK_CUSTOM_IDS.DESCRIPTION_FIELD)
|
||||
.setLabel("Description")
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.setPlaceholder("Provide detailed information about your feedback")
|
||||
.setRequired(true)
|
||||
.setMaxLength(1000);
|
||||
|
||||
const descriptionRow = new ActionRowBuilder<TextInputBuilder>().addComponents(descriptionInput);
|
||||
|
||||
modal.addComponents(titleRow, descriptionRow);
|
||||
|
||||
return modal;
|
||||
}
|
||||
|
||||
export function buildFeedbackMessage(feedback: FeedbackData) {
|
||||
// Define colors/themes for each feedback type
|
||||
const themes = {
|
||||
FEATURE_REQUEST: {
|
||||
icon: "💡",
|
||||
color: "Blue",
|
||||
title: "FEATURE REQUEST",
|
||||
description: "A new starlight suggestion has been received"
|
||||
},
|
||||
BUG_REPORT: {
|
||||
icon: "🐛",
|
||||
color: "Red",
|
||||
title: "BUG REPORT",
|
||||
description: "A cosmic anomaly has been detected"
|
||||
},
|
||||
GENERAL: {
|
||||
icon: "💬",
|
||||
color: "Gray",
|
||||
title: "GENERAL FEEDBACK",
|
||||
description: "A message from the cosmos"
|
||||
}
|
||||
};
|
||||
|
||||
const theme = themes[feedback.type];
|
||||
|
||||
if (!theme) {
|
||||
console.error(`Unknown feedback type: ${feedback.type}`);
|
||||
throw new Error(`Invalid feedback type: ${feedback.type}`);
|
||||
}
|
||||
|
||||
const timestamp = Math.floor(feedback.timestamp.getTime() / 1000);
|
||||
|
||||
// Header Container
|
||||
const headerContainer = new ContainerBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(`# ${theme.icon} ${theme.title}`),
|
||||
new TextDisplayBuilder().setContent(`*${theme.description}*`)
|
||||
);
|
||||
|
||||
// Content Container
|
||||
const contentContainer = new ContainerBuilder()
|
||||
.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(`## ${feedback.title}`),
|
||||
new TextDisplayBuilder().setContent(`> ${feedback.description.split('\n').join('\n> ')}`),
|
||||
new TextDisplayBuilder().setContent(
|
||||
`**Submitted by:** <@${feedback.userId}>\n**Time:** <t:${timestamp}:F> (<t:${timestamp}:R>)`
|
||||
)
|
||||
);
|
||||
|
||||
return [headerContainer, contentContainer];
|
||||
}
|
||||
Reference in New Issue
Block a user