AeThex-Bot-Master/aethex-bot/commands/trivia.js
sirpiglr c2a34f398e Add server mode configuration and dynamic status updates
Introduces a new server mode configuration system (Federation/Standalone) with associated command changes, dynamic status rotation for the bot, and adds new commands and features.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: b08e6ba5-7498-4b9f-b1c9-7dc11b362ddd
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/R9PkDi8
Replit-Helium-Checkpoint-Created: true
2025-12-09 23:26:33 +00:00

283 lines
9.1 KiB
JavaScript

const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const { getServerMode, getEmbedColor, EMBED_COLORS } = require('../utils/modeHelper');
const { updateStandaloneXp } = require('../utils/standaloneXp');
const TRIVIA_QUESTIONS = [
{
question: "What is the capital of Japan?",
answers: ["Tokyo", "Osaka", "Kyoto", "Hiroshima"],
correct: 0,
category: "Geography"
},
{
question: "Which planet is known as the Red Planet?",
answers: ["Venus", "Mars", "Jupiter", "Saturn"],
correct: 1,
category: "Science"
},
{
question: "What year did World War II end?",
answers: ["1943", "1944", "1945", "1946"],
correct: 2,
category: "History"
},
{
question: "What is the largest mammal on Earth?",
answers: ["African Elephant", "Blue Whale", "Giraffe", "Hippopotamus"],
correct: 1,
category: "Science"
},
{
question: "Who painted the Mona Lisa?",
answers: ["Michelangelo", "Leonardo da Vinci", "Raphael", "Donatello"],
correct: 1,
category: "Art"
},
{
question: "What is the chemical symbol for gold?",
answers: ["Go", "Gd", "Au", "Ag"],
correct: 2,
category: "Science"
},
{
question: "Which programming language was created by Brendan Eich?",
answers: ["Python", "Java", "JavaScript", "Ruby"],
correct: 2,
category: "Technology"
},
{
question: "What is the smallest country in the world?",
answers: ["Monaco", "Vatican City", "San Marino", "Liechtenstein"],
correct: 1,
category: "Geography"
},
{
question: "How many bones are in the adult human body?",
answers: ["186", "206", "226", "246"],
correct: 1,
category: "Science"
},
{
question: "What year was the first iPhone released?",
answers: ["2005", "2006", "2007", "2008"],
correct: 2,
category: "Technology"
},
{
question: "Which element has the atomic number 1?",
answers: ["Helium", "Hydrogen", "Oxygen", "Carbon"],
correct: 1,
category: "Science"
},
{
question: "What is the capital of Australia?",
answers: ["Sydney", "Melbourne", "Canberra", "Perth"],
correct: 2,
category: "Geography"
},
{
question: "Who wrote 'Romeo and Juliet'?",
answers: ["Charles Dickens", "William Shakespeare", "Jane Austen", "Mark Twain"],
correct: 1,
category: "Literature"
},
{
question: "What is the speed of light in km/s (approximately)?",
answers: ["150,000", "200,000", "300,000", "400,000"],
correct: 2,
category: "Science"
},
{
question: "Which company created Discord?",
answers: ["Hammer & Chisel", "Meta", "Microsoft", "Google"],
correct: 0,
category: "Technology"
}
];
const activeTrivia = new Map();
module.exports = {
data: new SlashCommandBuilder()
.setName('trivia')
.setDescription('Answer trivia questions to earn XP!')
.addStringOption(option =>
option.setName('category')
.setDescription('Choose a category (optional)')
.setRequired(false)
.addChoices(
{ name: 'All', value: 'all' },
{ name: 'Science', value: 'Science' },
{ name: 'Geography', value: 'Geography' },
{ name: 'History', value: 'History' },
{ name: 'Technology', value: 'Technology' },
{ name: 'Art', value: 'Art' },
{ name: 'Literature', value: 'Literature' }
)
),
async execute(interaction, supabase, client) {
const category = interaction.options.getString('category') || 'all';
const mode = await getServerMode(supabase, interaction.guildId);
const userId = interaction.user.id;
const guildId = interaction.guildId;
const activeKey = `${guildId}-${userId}`;
if (activeTrivia.has(activeKey)) {
return interaction.reply({
content: 'You already have an active trivia question! Answer it first.',
ephemeral: true
});
}
let questions = TRIVIA_QUESTIONS;
if (category !== 'all') {
questions = TRIVIA_QUESTIONS.filter(q => q.category === category);
}
if (questions.length === 0) {
return interaction.reply({
content: 'No questions available for that category.',
ephemeral: true
});
}
const question = questions[Math.floor(Math.random() * questions.length)];
const shuffledIndexes = [0, 1, 2, 3].sort(() => Math.random() - 0.5);
const shuffledAnswers = shuffledIndexes.map(i => question.answers[i]);
const correctIndex = shuffledIndexes.indexOf(question.correct);
activeTrivia.set(activeKey, {
correctIndex,
question: question.question,
startTime: Date.now()
});
const embed = new EmbedBuilder()
.setColor(getEmbedColor(mode))
.setTitle('🧠 Trivia Time!')
.setDescription(`**${question.question}**`)
.addFields(
{ name: '📚 Category', value: question.category, inline: true },
{ name: '⏱️ Time Limit', value: '30 seconds', inline: true },
{ name: '🎁 Reward', value: '25-50 XP', inline: true }
)
.setFooter({ text: 'Click a button to answer!' })
.setTimestamp();
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`trivia_0_${interaction.user.id}`)
.setLabel(shuffledAnswers[0])
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId(`trivia_1_${interaction.user.id}`)
.setLabel(shuffledAnswers[1])
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId(`trivia_2_${interaction.user.id}`)
.setLabel(shuffledAnswers[2])
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId(`trivia_3_${interaction.user.id}`)
.setLabel(shuffledAnswers[3])
.setStyle(ButtonStyle.Primary)
);
const message = await interaction.reply({ embeds: [embed], components: [row], fetchReply: true });
const collector = message.createMessageComponentCollector({
filter: i => i.customId.startsWith('trivia_') && i.customId.endsWith(`_${interaction.user.id}`),
time: 30000,
max: 1
});
collector.on('collect', async (i) => {
const triviaData = activeTrivia.get(activeKey);
if (!triviaData) return;
const selectedIndex = parseInt(i.customId.split('_')[1]);
const isCorrect = selectedIndex === triviaData.correctIndex;
const timeTaken = (Date.now() - triviaData.startTime) / 1000;
activeTrivia.delete(activeKey);
let xpReward = 0;
if (isCorrect) {
xpReward = timeTaken < 5 ? 50 : timeTaken < 15 ? 35 : 25;
if (mode === 'standalone') {
await updateStandaloneXp(supabase, userId, guildId, xpReward, interaction.user.username);
} else if (supabase) {
try {
const { data: profile } = await supabase
.from('user_profiles')
.select('xp')
.eq('discord_id', userId)
.maybeSingle();
if (profile) {
await supabase
.from('user_profiles')
.update({ xp: (profile.xp || 0) + xpReward })
.eq('discord_id', userId);
}
} catch (e) {}
}
}
const resultEmbed = new EmbedBuilder()
.setColor(isCorrect ? EMBED_COLORS.success : EMBED_COLORS.error)
.setTitle(isCorrect ? '✅ Correct!' : '❌ Incorrect!')
.setDescription(isCorrect
? `Great job! You answered in ${timeTaken.toFixed(1)} seconds.`
: `The correct answer was: **${question.answers[question.correct]}**`
)
.addFields(
{ name: '❓ Question', value: question.question }
)
.setTimestamp();
if (isCorrect) {
resultEmbed.addFields({ name: '🎁 XP Earned', value: `+${xpReward} XP`, inline: true });
}
const disabledRow = new ActionRowBuilder()
.addComponents(
...row.components.map((btn, idx) =>
ButtonBuilder.from(btn)
.setStyle(idx === triviaData.correctIndex ? ButtonStyle.Success :
(idx === selectedIndex && !isCorrect) ? ButtonStyle.Danger :
ButtonStyle.Secondary)
.setDisabled(true)
)
);
await i.update({ embeds: [resultEmbed], components: [disabledRow] });
});
collector.on('end', async (collected) => {
if (collected.size === 0) {
activeTrivia.delete(activeKey);
const timeoutEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.warning)
.setTitle('⏰ Time\'s Up!')
.setDescription(`You didn't answer in time. The correct answer was: **${question.answers[question.correct]}**`)
.setTimestamp();
const disabledRow = new ActionRowBuilder()
.addComponents(
...row.components.map(btn =>
ButtonBuilder.from(btn)
.setStyle(ButtonStyle.Secondary)
.setDisabled(true)
)
);
await interaction.editReply({ embeds: [timeoutEmbed], components: [disabledRow] });
}
});
},
};