Files
AuroraBot-discord/bot/commands/economy/trivia.ts

118 lines
5.3 KiB
TypeScript

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 <t:${timestamp}:R>.`
)],
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
});
}
}
}
}
});