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
283 lines
9.1 KiB
JavaScript
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] });
|
|
}
|
|
});
|
|
},
|
|
};
|