import { createCommand } from "@shared/lib/utils"; import { SlashCommandBuilder } from "discord.js"; import { triviaService } from "@shared/modules/trivia/trivia.service"; import { getTriviaQuestionView } from "@/modules/trivia/trivia.view"; import { createErrorEmbed } from "@lib/embeds"; import { UserError } from "@/lib/errors"; import { config } from "@shared/lib/config"; import { TriviaCategory } from "@shared/lib/constants"; export const trivia = createCommand({ data: new SlashCommandBuilder() .setName("trivia") .setDescription("Play trivia to win currency! Answer correctly within the time limit.") .addStringOption(option => option.setName('category') .setDescription('Select a specific category') .setRequired(false) .addChoices( { name: 'General Knowledge', value: String(TriviaCategory.GENERAL_KNOWLEDGE) }, { name: 'Books', value: String(TriviaCategory.BOOKS) }, { name: 'Film', value: String(TriviaCategory.FILM) }, { name: 'Music', value: String(TriviaCategory.MUSIC) }, { name: 'Video Games', value: String(TriviaCategory.VIDEO_GAMES) }, { name: 'Science & Nature', value: String(TriviaCategory.SCIENCE_NATURE) }, { name: 'Computers', value: String(TriviaCategory.COMPUTERS) }, { name: 'Mathematics', value: String(TriviaCategory.MATHEMATICS) }, { name: 'Mythology', value: String(TriviaCategory.MYTHOLOGY) }, { name: 'Sports', value: String(TriviaCategory.SPORTS) }, { name: 'Geography', value: String(TriviaCategory.GEOGRAPHY) }, { name: 'History', value: String(TriviaCategory.HISTORY) }, { name: 'Politics', value: String(TriviaCategory.POLITICS) }, { name: 'Art', value: String(TriviaCategory.ART) }, { name: 'Animals', value: String(TriviaCategory.ANIMALS) }, { name: 'Anime & Manga', value: String(TriviaCategory.ANIME_MANGA) }, ) ), execute: async (interaction) => { try { const categoryId = interaction.options.getString('category'); // Check if user can play BEFORE deferring const canPlay = await triviaService.canPlayTrivia(interaction.user.id); if (!canPlay.canPlay) { // Cooldown error - ephemeral const timestamp = Math.floor(canPlay.nextAvailable!.getTime() / 1000); await interaction.reply({ embeds: [createErrorEmbed( `You're on cooldown! Try again .` )], ephemeral: true }); return; } // User can play - defer publicly for trivia question await interaction.deferReply(); // Start trivia session (deducts entry fee) const session = await triviaService.startTrivia( interaction.user.id, interaction.user.username, categoryId ? parseInt(categoryId) : undefined ); // Generate Components v2 message const { components, flags } = getTriviaQuestionView(session, interaction.user.username); // Reply with Components v2 question await interaction.editReply({ components, flags }); // Set up automatic timeout cleanup setTimeout(async () => { const stillActive = triviaService.getSession(session.sessionId); if (stillActive) { // User didn't answer - clean up session with no reward try { await triviaService.submitAnswer(session.sessionId, interaction.user.id, false); } catch (error) { // Session already cleaned up, ignore } } }, config.trivia.timeoutSeconds * 1000 + 5000); // 5 seconds grace period } catch (error: any) { if (error instanceof UserError) { // Check if we've already deferred if (interaction.deferred) { await interaction.editReply({ embeds: [createErrorEmbed(error.message)] }); } else { await interaction.reply({ embeds: [createErrorEmbed(error.message)], ephemeral: true }); } } else { console.error("Error in trivia command:", error); // Check if we've already deferred if (interaction.deferred) { await interaction.editReply({ embeds: [createErrorEmbed("An unexpected error occurred. Please try again later.")] }); } else { await interaction.reply({ embeds: [createErrorEmbed("An unexpected error occurred. Please try again later.")], ephemeral: true }); } } } } });