feat: Implement interactive quest command allowing users to view active/available quests and accept new ones.

This commit is contained in:
syntaxbullet
2026-01-15 15:30:01 +01:00
parent eb108695d3
commit 9e5c6b5ac3
4 changed files with 212 additions and 14 deletions

View File

@@ -1,25 +1,74 @@
import { createCommand } from "@shared/lib/utils";
import { SlashCommandBuilder, MessageFlags } from "discord.js";
import { SlashCommandBuilder, MessageFlags, ComponentType } from "discord.js";
import { questService } from "@shared/modules/quest/quest.service";
import { createWarningEmbed } from "@lib/embeds";
import { getQuestListEmbed } from "@/modules/quest/quest.view";
import { createSuccessEmbed, createWarningEmbed } from "@lib/embeds";
import { getQuestListEmbed, getAvailableQuestsEmbed, getQuestActionRows } from "@/modules/quest/quest.view";
export const quests = createCommand({
data: new SlashCommandBuilder()
.setName("quests")
.setDescription("View your active quests"),
.setDescription("View your active and available quests"),
execute: async (interaction) => {
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const response = await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const userQuests = await questService.getUserQuests(interaction.user.id);
const userId = interaction.user.id;
if (!userQuests || userQuests.length === 0) {
await interaction.editReply({ embeds: [createWarningEmbed("You have no active quests.", "Quest Log")] });
return;
}
const updateView = async (viewType: 'active' | 'available') => {
const userQuests = await questService.getUserQuests(userId);
const availableQuests = await questService.getAvailableQuests(userId);
const embed = getQuestListEmbed(userQuests);
const embed = viewType === 'active'
? getQuestListEmbed(userQuests)
: getAvailableQuestsEmbed(availableQuests);
const components = getQuestActionRows(viewType, availableQuests);
await interaction.editReply({ embeds: [embed] });
await interaction.editReply({
embeds: [embed],
components: components
});
};
// Initial view
await updateView('active');
const collector = response.createMessageComponentCollector({
time: 60000,
componentType: undefined // Allow both buttons and select menu
});
collector.on('collect', async (i) => {
if (i.user.id !== interaction.user.id) return;
try {
if (i.customId === "quest_view_active") {
await i.deferUpdate();
await updateView('active');
} else if (i.customId === "quest_view_available") {
await i.deferUpdate();
await updateView('available');
} else if (i.customId === "quest_accept_select") {
const questId = parseInt((i as any).values[0]);
await questService.assignQuest(userId, questId);
await i.reply({
embeds: [createSuccessEmbed(`You have accepted a new quest!`, "Quest Accepted")],
flags: MessageFlags.Ephemeral
});
await updateView('active');
}
} catch (error) {
console.error("Quest interaction error:", error);
await i.followUp({
content: "Something went wrong while processing your quest interaction.",
flags: MessageFlags.Ephemeral
});
}
});
collector.on('end', () => {
interaction.editReply({ components: [] }).catch(() => {});
});
}
});