feat: trivia command!
This commit is contained in:
334
bot/modules/trivia/trivia.view.ts
Normal file
334
bot/modules/trivia/trivia.view.ts
Normal file
@@ -0,0 +1,334 @@
|
||||
import { MessageFlags } from "discord.js";
|
||||
import type { TriviaSession, TriviaResult } from "@shared/modules/trivia/trivia.service";
|
||||
|
||||
/**
|
||||
* Get color based on difficulty level
|
||||
*/
|
||||
function getDifficultyColor(difficulty: string): number {
|
||||
switch (difficulty.toLowerCase()) {
|
||||
case 'easy':
|
||||
return 0x57F287; // Green
|
||||
case 'medium':
|
||||
return 0xFEE75C; // Yellow
|
||||
case 'hard':
|
||||
return 0xED4245; // Red
|
||||
default:
|
||||
return 0x5865F2; // Blurple
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get emoji for difficulty level
|
||||
*/
|
||||
function getDifficultyEmoji(difficulty: string): string {
|
||||
switch (difficulty.toLowerCase()) {
|
||||
case 'easy':
|
||||
return '🟢';
|
||||
case 'medium':
|
||||
return '🟡';
|
||||
case 'hard':
|
||||
return '🔴';
|
||||
default:
|
||||
return '⭐';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Components v2 message for a trivia question
|
||||
*/
|
||||
export function getTriviaQuestionView(session: TriviaSession, username: string): {
|
||||
components: any[];
|
||||
flags: number;
|
||||
} {
|
||||
const { question, allAnswers, entryFee, potentialReward, expiresAt, sessionId } = session;
|
||||
|
||||
// Calculate time remaining
|
||||
const now = Date.now();
|
||||
const timeLeft = Math.max(0, expiresAt.getTime() - now);
|
||||
const secondsLeft = Math.floor(timeLeft / 1000);
|
||||
|
||||
const difficultyEmoji = getDifficultyEmoji(question.difficulty);
|
||||
const difficultyText = question.difficulty.charAt(0).toUpperCase() + question.difficulty.slice(1);
|
||||
const accentColor = getDifficultyColor(question.difficulty);
|
||||
|
||||
const components: any[] = [];
|
||||
|
||||
// Main Container with difficulty accent color
|
||||
components.push({
|
||||
type: 17, // Container
|
||||
accent_color: accentColor,
|
||||
components: [
|
||||
// Title and metadata section
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `# 🎯 Trivia Challenge\n**${difficultyEmoji} ${difficultyText}** • 📚 ${question.category}`
|
||||
},
|
||||
// Separator
|
||||
{
|
||||
type: 14, // Separator
|
||||
spacing: 1,
|
||||
divider: true
|
||||
},
|
||||
// Question
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `### ${question.question}`
|
||||
},
|
||||
// Stats section
|
||||
{
|
||||
type: 14, // Separator
|
||||
spacing: 1,
|
||||
divider: false
|
||||
},
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `⏱️ **Time:** <t:${Math.floor(expiresAt.getTime() / 1000)}:R> (${secondsLeft}s)\n💰 **Stakes:** ${entryFee} AU ➜ ${potentialReward} AU\n👤 **Player:** ${username}`
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Answer buttons
|
||||
if (question.type === 'boolean') {
|
||||
const trueIndex = allAnswers.indexOf('True');
|
||||
const falseIndex = allAnswers.indexOf('False');
|
||||
|
||||
components.push({
|
||||
type: 1, // Action Row
|
||||
components: [
|
||||
{
|
||||
type: 2, // Button
|
||||
custom_id: `trivia_answer_${sessionId}_${trueIndex}`,
|
||||
label: 'True',
|
||||
style: 3, // Success
|
||||
emoji: { name: '✅' }
|
||||
},
|
||||
{
|
||||
type: 2, // Button
|
||||
custom_id: `trivia_answer_${sessionId}_${falseIndex}`,
|
||||
label: 'False',
|
||||
style: 4, // Danger
|
||||
emoji: { name: '❌' }
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
const labels = ['A', 'B', 'C', 'D'];
|
||||
const emojis = ['🇦', '🇧', '🇨', '🇩'];
|
||||
|
||||
const buttonRow: any = {
|
||||
type: 1, // Action Row
|
||||
components: []
|
||||
};
|
||||
|
||||
for (let i = 0; i < allAnswers.length && i < 4; i++) {
|
||||
const label = labels[i];
|
||||
const emoji = emojis[i];
|
||||
const answer = allAnswers[i];
|
||||
|
||||
if (!label || !emoji || !answer) continue;
|
||||
|
||||
buttonRow.components.push({
|
||||
type: 2, // Button
|
||||
custom_id: `trivia_answer_${sessionId}_${i}`,
|
||||
label: `${label}: ${answer.substring(0, 30)}${answer.length > 30 ? '...' : ''}`,
|
||||
style: 2, // Secondary
|
||||
emoji: { name: emoji }
|
||||
});
|
||||
}
|
||||
|
||||
components.push(buttonRow);
|
||||
}
|
||||
|
||||
// Give Up button in separate row
|
||||
components.push({
|
||||
type: 1, // Action Row
|
||||
components: [
|
||||
{
|
||||
type: 2, // Button
|
||||
custom_id: `trivia_giveup_${sessionId}`,
|
||||
label: 'Give Up',
|
||||
style: 4, // Danger
|
||||
emoji: { name: '🏳️' }
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
components,
|
||||
flags: MessageFlags.IsComponentsV2
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Components v2 result message
|
||||
*/
|
||||
export function getTriviaResultView(
|
||||
result: TriviaResult,
|
||||
question: string,
|
||||
userAnswer?: string,
|
||||
allAnswers?: string[]
|
||||
): {
|
||||
components: any[];
|
||||
flags: number;
|
||||
} {
|
||||
const { correct, reward, correctAnswer } = result;
|
||||
const components: any[] = [];
|
||||
|
||||
if (correct) {
|
||||
// Success container
|
||||
components.push({
|
||||
type: 17, // Container
|
||||
accent_color: 0x57F287, // Green
|
||||
components: [
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `# 🎉 Correct Answer!\n### ${question}`
|
||||
},
|
||||
{
|
||||
type: 14, // Separator
|
||||
spacing: 1,
|
||||
divider: true
|
||||
},
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `✅ **Your answer:** ${correctAnswer}\n\n💰 **Reward:** +${reward} AU\n\n🏆 Great job! Keep it up!`
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
const answerDisplay = userAnswer
|
||||
? `❌ **Your answer:** ${userAnswer}\n✅ **Correct answer:** ${correctAnswer}`
|
||||
: `✅ **Correct answer:** ${correctAnswer}`;
|
||||
|
||||
// Error container
|
||||
components.push({
|
||||
type: 17, // Container
|
||||
accent_color: 0xED4245, // Red
|
||||
components: [
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `# ❌ Incorrect Answer\n### ${question}`
|
||||
},
|
||||
{
|
||||
type: 14, // Separator
|
||||
spacing: 1,
|
||||
divider: true
|
||||
},
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `${answerDisplay}\n\n💸 **Entry fee lost:** ${reward} AU\n\n📚 Better luck next time!`
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Show disabled buttons with visual feedback
|
||||
if (allAnswers && allAnswers.length > 0) {
|
||||
const buttonRow: any = {
|
||||
type: 1, // Action Row
|
||||
components: []
|
||||
};
|
||||
|
||||
const labels = ['A', 'B', 'C', 'D'];
|
||||
const emojis = ['🇦', '🇧', '🇨', '🇩'];
|
||||
|
||||
for (let i = 0; i < allAnswers.length && i < 4; i++) {
|
||||
const label = labels[i];
|
||||
const emoji = emojis[i];
|
||||
const answer = allAnswers[i];
|
||||
|
||||
if (!label || !emoji || !answer) continue;
|
||||
|
||||
const isCorrect = answer === correctAnswer;
|
||||
const wasUserAnswer = answer === userAnswer;
|
||||
|
||||
buttonRow.components.push({
|
||||
type: 2, // Button
|
||||
custom_id: `trivia_result_${i}`,
|
||||
label: `${label}: ${answer.substring(0, 30)}${answer.length > 30 ? '...' : ''}`,
|
||||
style: isCorrect ? 3 : wasUserAnswer ? 4 : 2, // Success : Danger : Secondary
|
||||
emoji: { name: isCorrect ? '✅' : wasUserAnswer ? '❌' : emoji },
|
||||
disabled: true
|
||||
});
|
||||
}
|
||||
|
||||
components.push(buttonRow);
|
||||
}
|
||||
|
||||
return {
|
||||
components,
|
||||
flags: MessageFlags.IsComponentsV2
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Components v2 timeout message
|
||||
*/
|
||||
export function getTriviaTimeoutView(
|
||||
question: string,
|
||||
correctAnswer: string,
|
||||
allAnswers?: string[]
|
||||
): {
|
||||
components: any[];
|
||||
flags: number;
|
||||
} {
|
||||
const components: any[] = [];
|
||||
|
||||
// Timeout container
|
||||
components.push({
|
||||
type: 17, // Container
|
||||
accent_color: 0xFEE75C, // Yellow
|
||||
components: [
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `# ⏱️ Time's Up!\n### ${question}`
|
||||
},
|
||||
{
|
||||
type: 14, // Separator
|
||||
spacing: 1,
|
||||
divider: true
|
||||
},
|
||||
{
|
||||
type: 10, // Text Display
|
||||
content: `⏰ **You ran out of time!**\n✅ **Correct answer:** ${correctAnswer}\n\n💸 Entry fee lost\n\n⚡ Be faster next time!`
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Show disabled buttons with correct answer highlighted
|
||||
if (allAnswers && allAnswers.length > 0) {
|
||||
const buttonRow: any = {
|
||||
type: 1, // Action Row
|
||||
components: []
|
||||
};
|
||||
|
||||
const labels = ['A', 'B', 'C', 'D'];
|
||||
const emojis = ['🇦', '🇧', '🇨', '🇩'];
|
||||
|
||||
for (let i = 0; i < allAnswers.length && i < 4; i++) {
|
||||
const label = labels[i];
|
||||
const emoji = emojis[i];
|
||||
const answer = allAnswers[i];
|
||||
|
||||
if (!label || !emoji || !answer) continue;
|
||||
|
||||
const isCorrect = answer === correctAnswer;
|
||||
|
||||
buttonRow.components.push({
|
||||
type: 2, // Button
|
||||
custom_id: `trivia_timeout_${i}`,
|
||||
label: `${label}: ${answer.substring(0, 30)}${answer.length > 30 ? '...' : ''}`,
|
||||
style: isCorrect ? 3 : 2, // Success : Secondary
|
||||
emoji: { name: isCorrect ? '✅' : emoji },
|
||||
disabled: true
|
||||
});
|
||||
}
|
||||
|
||||
components.push(buttonRow);
|
||||
}
|
||||
|
||||
return {
|
||||
components,
|
||||
flags: MessageFlags.IsComponentsV2
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user