118 lines
5.3 KiB
TypeScript
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
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|