7dtd_triviaTime
- community
- minigames
- by limon
- Takaro v0.2.1
- 7 Days to Die
Limon_triviaTime: Interactive Trivia Game System
The Limon_triviaTime module transforms your gaming server with an engaging trivia game system that automatically poses questions and rewards correct answers. This enhanced version supports both the Open Trivia Database API and custom questions.
Key Benefits:
- Dual Question Sources: Choose between Open Trivia Database API or custom questions
- Rich Category Selection: Access to 20+ categories from the API including General Knowledge, Science, Sports, History, and more
- Difficulty Control: Easy, Medium, and Hard difficulty levels
- Flexible Question Types: Support for True/False and Multiple Choice questions
- Flexible Reward System: Choose between currency or item rewards for winners
- Automated Scheduling: Set trivia events to run at specified intervals
- Player Engagement Tool: Keeps your community active and entertained
- Simple Answer Validation: Easy command-based answer submission system
Features:
- Open Trivia Database API integration with 4000+ verified questions
- Configurable question database with pre-populated examples as fallback
- Dual reward types (currency or items)
- Customizable item quality and quantity settings
- Permission-based participation control
- Automated trivia scheduling with cron jobs
- Real-time feedback on incorrect answers
- Server-wide announcements for winners
- HTML entity decoding for proper question display
- Session token support to prevent duplicate questions
Ideal for server administrators looking to increase player retention and build community through interactive gameplay elements. Works seamlessly with existing Takaro economy and item systems.
Configuration 11
Settings you fill in when installing the module on a server.
| Setting | Type | Default | Description |
|---|---|---|---|
questionSource Question Source | string | "api" | Choose between Open Trivia Database API or custom questions |
apiCategory API Categories | array | ["any"] | Categories for Open Trivia Database questions (select multiple or 'any') |
apiDifficulty API Difficulty | string | "any" | Difficulty level for Open Trivia Database questions |
apiType Question Type | string | "any" | Type of questions from API |
rewardType Reward Type | string | "currency" | Choose Currency, Items, or Both (random) |
rewardAmount Reward Amount | number | 100 | Currency reward amount |
rewardItems Items | array | — | Items to give as rewards |
questions Custom Questions (Fallback) | array | [{"question":"What is the capital of France?","answer":"Paris"},{"question":"What is the largest planet in our solar system?","answer":"Jupiter"}] | Used when API is unavailable or question source is set to custom |
triviaStartSound Trivia Start Sound | string | "ui_click" | Sound to play to all players when a new trivia question is posted (leave empty to disable) |
correctAnswerSound Correct Answer Sound | string | "ui_unlock" | Sound to play to all players when someone answers correctly (leave empty to disable) |
wrongAnswerSound Wrong Answer Sound | string | "ui_denied" | Sound to play to the player when they answer incorrectly (leave empty to disable) |
Raw config schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"questionSource": {
"title": "Question Source",
"description": "Choose between Open Trivia Database API or custom questions",
"type": "string",
"enum": [
"api",
"custom"
],
"default": "api"
},
"apiCategory": {
"title": "API Categories",
"description": "Categories for Open Trivia Database questions (select multiple or 'any')",
"type": "array",
"items": {
"type": "string",
"enum": [
"any",
"general_knowledge",
"books",
"film",
"music",
"musicals_theatres",
"television",
"video_games",
"board_games",
"science_nature",
"computers",
"mathematics",
"mythology",
"sports",
"geography",
"history",
"politics",
"art",
"celebrities",
"animals",
"vehicles",
"comics",
"gadgets",
"anime_manga",
"cartoon_animations"
]
},
"uniqueItems": true,
"default": [
"any"
]
},
"apiDifficulty": {
"title": "API Difficulty",
"description": "Difficulty level for Open Trivia Database questions",
"type": "string",
"enum": [
"any",
"easy",
"medium",
"hard"
],
"default": "any"
},
"apiType": {
"title": "Question Type",
"description": "Type of questions from API",
"type": "string",
"enum": [
"any",
"multiple",
"boolean"
],
"default": "any"
},
"rewardType": {
"title": "Reward Type",
"description": "Choose Currency, Items, or Both (random)",
"default": "currency",
"type": "string",
"enum": [
"currency",
"items",
"both"
]
},
"rewardAmount": {
"title": "Reward Amount",
"description": "Currency reward amount",
"default": 100,
"type": "number",
"minimum": 1,
"maximum": 100000
},
"rewardItems": {
"title": "Items",
"description": "Items to give as rewards",
"x-component": "item",
"type": "array",
"uniqueItems": true,
"items": {
"type": "object",
"title": "Item",
"properties": {
"item": {
"type": "string",
"title": "Item"
},
"amount": {
"type": "number",
"title": "Amount"
},
"quality": {
"type": "string",
"title": "Quality"
}
}
}
},
"questions": {
"type": "array",
"title": "Custom Questions (Fallback)",
"description": "Used when API is unavailable or question source is set to custom",
"items": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The question",
"minLength": 1
},
"answer": {
"type": "string",
"description": "The answer"
}
}
},
"default": [
{
"question": "What is the capital of France?",
"answer": "Paris"
},
{
"question": "What is the largest planet in our solar system?",
"answer": "Jupiter"
}
]
},
"triviaStartSound": {
"title": "Trivia Start Sound",
"description": "Sound to play to all players when a new trivia question is posted (leave empty to disable)",
"type": "string",
"default": "ui_click"
},
"correctAnswerSound": {
"title": "Correct Answer Sound",
"description": "Sound to play to all players when someone answers correctly (leave empty to disable)",
"type": "string",
"default": "ui_unlock"
},
"wrongAnswerSound": {
"title": "Wrong Answer Sound",
"description": "Sound to play to the player when they answer incorrectly (leave empty to disable)",
"type": "string",
"default": "ui_denied"
}
},
"additionalProperties": false
} Raw UI schema
{
"rewardItems": {
"items": {
"item": {
"ui:widget": "item"
}
}
}
} Commands 3
Chat commands players trigger in game.
-
playerAnswer
Submit your answer to the current trivia question
Argument Type Default Help answerstring provide an answer Command source
import { takaro, data, TakaroUserError } from '@takaro/helpers'; async function main() { const { player, arguments: args, gameServerId, module: mod, pog } = data; // Helper function to play sound to all online players async function playSoundToPlayers(gameServerId, soundId, repeats = 1) { if (!soundId || soundId.trim() === '') { return; // Skip if no sound specified } // Get all online players const onlinePlayersRes = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({ filters: { gameServerId: [gameServerId], online: [true] } }); for (const pogPlayer of onlinePlayersRes.data.data) { const targetId = pogPlayer.gameId; if (!targetId) { console.log('No valid player ID found, skipping player'); continue; } const command = `playsound EOS_${targetId} ${soundId}`; for (let i = 0; i < repeats; i++) { await takaro.gameserver.gameServerControllerExecuteCommand(gameServerId, { command: command }); } } } // Helper function to play sound to a specific player async function playSoundToPlayer(gameServerId, playerId, soundId, repeats = 1) { if (!soundId || soundId.trim() === '') { return; // Skip if no sound specified } // Get the specific player's game ID const playerRes = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({ filters: { gameServerId: [gameServerId], playerId: [playerId], online: [true] } }); if (playerRes.data.data.length === 0) { console.log('Player not found or not online'); return; } const targetId = playerRes.data.data[0].gameId; if (!targetId) { console.log('No valid player ID found'); return; } const command = `playsound EOS_${targetId} ${soundId}`; for (let i = 0; i < repeats; i++) { await takaro.gameserver.gameServerControllerExecuteCommand(gameServerId, { command: command }); } } if (!args.answer || args.answer.trim() === '') { throw new TakaroUserError('Please provide an answer!'); } // Check if there's an active trivia question const questionVariable = await takaro.variable.variableControllerSearch({ filters: { key: ['trivia_question'], gameServerId: [gameServerId], moduleId: [mod.moduleId] } }); if (questionVariable.data.data.length === 0) { throw new TakaroUserError('No active trivia question! Wait for the next one.'); } // Get the correct answer const answerVariable = await takaro.variable.variableControllerSearch({ filters: { key: ['trivia_answer'], gameServerId: [gameServerId], moduleId: [mod.moduleId] } }); if (answerVariable.data.data.length === 0) { throw new TakaroUserError('No answer found for the current question.'); } // Get reward info const rewardVariable = await takaro.variable.variableControllerSearch({ filters: { key: ['trivia_reward'], gameServerId: [gameServerId], moduleId: [mod.moduleId] } }); if (rewardVariable.data.data.length === 0) { throw new TakaroUserError('No reward configured for this trivia.'); } const correctAnswer = answerVariable.data.data[0].value; const playerAnswer = args.answer.trim(); // Normalize answers for comparison (lowercase, remove extra spaces) const normalizeAnswer = (answer) => { return answer.toLowerCase().replace(/\s+/g, ' ').trim(); }; const normalizedCorrectAnswer = normalizeAnswer(correctAnswer); const normalizedPlayerAnswer = normalizeAnswer(playerAnswer); console.log(`Player ${player.name} answered: "${playerAnswer}" (normalized: "${normalizedPlayerAnswer}")`); console.log(`Correct answer: "${correctAnswer}" (normalized: "${normalizedCorrectAnswer}")`); // Check if the answer is correct if (normalizedPlayerAnswer === normalizedCorrectAnswer) { // Give reward const rewardData = JSON.parse(rewardVariable.data.data[0].value); let rewardMessage = ''; if (rewardData.type === 'currency') { await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, player.id, { currency: rewardData.amount }); const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value; rewardMessage = `You won ${rewardData.amount} ${currencyName}!`; } else if (rewardData.type === 'items') { const item = (await takaro.item.itemControllerFindOne(rewardData.itemId)).data.data; await takaro.gameserver.gameServerControllerGiveItem(gameServerId, player.id, { name: item.code, amount: rewardData.amount, quality: rewardData.quality || '' }); rewardMessage = `You won ${rewardData.amount}x ${item.name}!`; } // 🆕 WIN TRACKING - Record the player's win const winKey = `trivia_wins_${player.id}`; const existingWins = await takaro.variable.variableControllerSearch({ filters: { key: [winKey], gameServerId: [gameServerId], moduleId: [mod.moduleId] } }); let totalWins = 1; if (existingWins.data.data.length > 0) { totalWins = parseInt(existingWins.data.data[0].value) + 1; await takaro.variable.variableControllerUpdate(existingWins.data.data[0].id, { value: totalWins.toString() }); } else { await takaro.variable.variableControllerCreate({ key: winKey, value: '1', gameServerId, moduleId: mod.moduleId, playerId: player.id }); } // Success message with total wins await takaro.gameserver.gameServerControllerSendMessage(gameServerId, { message: `🎉 ${player.name} got it right! The answer was: ${correctAnswer}. ${rewardMessage} (Total wins: ${totalWins})` }); // Play correct answer sound to all online players const correctAnswerSound = mod.userConfig.correctAnswerSound; if (correctAnswerSound) { await playSoundToPlayers(gameServerId, correctAnswerSound, 1); } // Clean up trivia variables const variablesToClean = await takaro.variable.variableControllerSearch({ filters: { key: ['trivia_question', 'trivia_answer', 'trivia_reward', 'trivia_type', 'trivia_incorrect_answers'], gameServerId: [gameServerId], moduleId: [mod.moduleId] } }); await Promise.all(variablesToClean.data.data.map(variable => takaro.variable.variableControllerDelete(variable.id) )); console.log(`${player.name} answered correctly and won! Total wins: ${totalWins}`); } else { // Wrong answer - play sound only to the player who answered incorrectly const wrongAnswerSound = mod.userConfig.wrongAnswerSound; if (wrongAnswerSound) { await playSoundToPlayer(gameServerId, player.id, wrongAnswerSound, 1); } // Wrong answer message await player.pm(`❌ Sorry, "${playerAnswer}" is not correct. Try again!`); console.log(`${player.name} answered incorrectly.`); } } await main(); -
triviareset
No help text available
Command source
import { takaro, data, checkPermission, TakaroUserError } from '@takaro/helpers'; async function main() { const { gameServerId, module: mod, pog, player } = data; // Check permission if (!checkPermission(pog, 'TRIVIA_RESET_LEADERBOARD')) { throw new TakaroUserError('You do not have permission to reset the trivia leaderboard.'); } // Get all trivia-related variables for this server const triviaVariables = await takaro.variable.variableControllerSearch({ filters: { gameServerId: [gameServerId], moduleId: [mod.moduleId] }, search: { key: ['trivia_wins_', 'trivia_question', 'trivia_answer', 'trivia_reward', 'trivia_type', 'trivia_incorrect_answers'] }, limit: 1000 }); if (triviaVariables.data.data.length === 0) { await player.pm('🗑️ No trivia data found to reset.'); return; } // Delete all trivia variables const deletePromises = triviaVariables.data.data.map(variable => takaro.variable.variableControllerDelete(variable.id) ); await Promise.allSettled(deletePromises); // Send confirmation const winsCount = triviaVariables.data.data.filter(v => v.key.startsWith('trivia_wins_')).length; await player.pm(`🗑️ Trivia leaderboard reset successfully! Cleared ${winsCount} player records.`); // Announce to server await takaro.gameserver.gameServerControllerSendMessage(gameServerId, { message: `🔄 Trivia leaderboard has been reset by an administrator!` }); console.log(`Trivia leaderboard reset by player ${player.name} (${player.id})`); } await main(); -
triviaboard
No help text available
Command source
import { takaro, data } from '@takaro/helpers'; async function main() { const { gameServerId, module: mod, player } = data; // Get all trivia wins for this server const allWins = await takaro.variable.variableControllerSearch({ filters: { gameServerId: [gameServerId], moduleId: [mod.moduleId] }, search: { key: ['trivia_wins_'] }, limit: 1000 }); if (allWins.data.data.length === 0) { await player.pm('🏆 No trivia winners yet! Be the first to answer a question correctly!'); return; } // Sort by wins (stored as string, so convert to number) const sortedWins = allWins.data.data .map(record => ({ playerId: record.playerId, wins: parseInt(record.value) || 0 })) .sort((a, b) => b.wins - a.wins) .slice(0, mod.userConfig.leaderboardSize || 10); // Get player names const leaderboard = await Promise.all( sortedWins.map(async (record, index) => { const playerData = await takaro.player.playerControllerGetOne(record.playerId); return { position: index + 1, name: playerData.data.data.name, wins: record.wins }; }) ); // Build leaderboard message let message = '🏆 **Trivia Leaderboard** 🧠\n\n'; leaderboard.forEach(entry => { const medal = entry.position === 1 ? '🥇' : entry.position === 2 ? '🥈' : entry.position === 3 ? '🥉' : ' '; message += `${medal} ${entry.position}. ${entry.name} - ${entry.wins} win${entry.wins !== 1 ? 's' : ''}\n`; }); // Add current player's ranking if not in top list const currentPlayerWins = sortedWins.find(record => record.playerId === player.id); if (!currentPlayerWins && allWins.data.data.some(record => record.playerId === player.id)) { const playerRecord = allWins.data.data.find(record => record.playerId === player.id); const playerWins = parseInt(playerRecord.value) || 0; const playerRank = allWins.data.data .map(record => parseInt(record.value) || 0) .sort((a, b) => b - a) .findIndex(wins => wins <= playerWins) + 1; message += `\n📍 Your rank: #${playerRank} with ${playerWins} win${playerWins !== 1 ? 's' : ''}`; } await player.pm(message); } await main();
Cron jobs 1
Work the module runs on a schedule.
-
TriviaTime
Cron job source
import { takaro, data } from '@takaro/helpers'; async function main() { const { gameServerId, module: mod } = data; // Helper function to play sound to all online players async function playSoundToPlayers(gameServerId, players, soundId, repeats = 1) { if (!soundId || soundId.trim() === '') { return; // Skip if no sound specified } for (const pog of players) { const targetId = pog.gameId; if (!targetId) { console.log('No valid player ID found, skipping player'); continue; } const command = `playsound EOS_${targetId} ${soundId}`; for (let i = 0; i < repeats; i++) { await takaro.gameserver.gameServerControllerExecuteCommand(gameServerId, { command: command }); } } } // Check for online players const currentPlayersRes = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({ filters: { gameServerId: [gameServerId], online: [true] } }); if (currentPlayersRes.data.data.length === 0) { console.log('No online players, skipping trivia'); return; } console.log(`Found ${currentPlayersRes.data.data.length} online players`); // Clear existing variables first const existingVariables = await takaro.variable.variableControllerSearch({ filters: { key: ['trivia_question', 'trivia_answer', 'trivia_reward', 'trivia_type', 'trivia_incorrect_answers'], gameServerId: [gameServerId], moduleId: [mod.moduleId] } }); await Promise.all(existingVariables.data.data.map(variable => takaro.variable.variableControllerDelete(variable.id) )); let questionData = null; let questionSource = mod.userConfig.questionSource || 'api'; if (questionSource === 'api') { console.log('Attempting to fetch from Open Trivia Database API using Axios'); try { // Check if axios is available if (takaro.axios) { console.log('Axios is available! Making API request...'); // Build API URL let url = 'https://opentdb.com/api.php?amount=1'; // Add categories if specified const categories = mod.userConfig.apiCategory || ['any']; if (categories.length > 0 && !categories.includes('any')) { const categoryMap = { 'general_knowledge': 9, 'books': 10, 'film': 11, 'music': 12, 'musicals_theatres': 13, 'television': 14, 'video_games': 15, 'board_games': 16, 'science_nature': 17, 'computers': 18, 'mathematics': 19, 'mythology': 20, 'sports': 21, 'geography': 22, 'history': 23, 'politics': 24, 'art': 25, 'celebrities': 26, 'animals': 27, 'vehicles': 28, 'comics': 29, 'gadgets': 30, 'anime_manga': 31, 'cartoon_animations': 32 }; const randomCategory = categories[Math.floor(Math.random() * categories.length)]; const categoryId = categoryMap[randomCategory]; if (categoryId) { url += `&category=${categoryId}`; } } // Add difficulty if specified if (mod.userConfig.apiDifficulty && mod.userConfig.apiDifficulty !== 'any') { url += `&difficulty=${mod.userConfig.apiDifficulty}`; } // Add type if specified if (mod.userConfig.apiType && mod.userConfig.apiType !== 'any') { url += `&type=${mod.userConfig.apiType}`; } console.log('Fetching trivia question from:', url); // Make the API request using Axios const response = await takaro.axios.get(url); const apiData = response.data; console.log('API Response:', JSON.stringify(apiData)); if (apiData.response_code === 0 && apiData.results && apiData.results.length > 0) { const question = apiData.results[0]; questionData = { question: decodeHtmlEntities(question.question), answer: decodeHtmlEntities(question.correct_answer), type: question.type, incorrectAnswers: question.incorrect_answers ? question.incorrect_answers.map(decodeHtmlEntities) : [] }; console.log('Successfully fetched API question:', questionData.question); } else { console.log('API returned error or no results:', apiData); questionSource = 'custom'; } } else { console.log('Axios not available, falling back to custom questions'); questionSource = 'custom'; } } catch (error) { console.log('API failed, falling back to custom questions:', error.message); questionSource = 'custom'; } } if (questionSource === 'custom' || !questionData) { console.log('Using custom questions'); const questions = mod.userConfig.questions || [ { question: "What is the largest country in the world?", answer: "Russia" }, { question: "What is the capital of France?", answer: "Paris" }, { question: "What is 2 + 2?", answer: "4" } ]; const randomQuestion = questions[Math.floor(Math.random() * questions.length)]; questionData = { question: randomQuestion.question, answer: randomQuestion.answer, type: 'multiple', incorrectAnswers: [] }; } if (!questionData) { console.log('No question data available'); return; } // Store question data await takaro.variable.variableControllerCreate({ key: 'trivia_question', value: questionData.question, gameServerId, moduleId: mod.moduleId }); await takaro.variable.variableControllerCreate({ key: 'trivia_answer', value: questionData.answer, gameServerId, moduleId: mod.moduleId }); await takaro.variable.variableControllerCreate({ key: 'trivia_type', value: questionData.type, gameServerId, moduleId: mod.moduleId }); if (questionData.incorrectAnswers.length > 0) { await takaro.variable.variableControllerCreate({ key: 'trivia_incorrect_answers', value: JSON.stringify(questionData.incorrectAnswers), gameServerId, moduleId: mod.moduleId }); } // Setup reward - Updated to handle "both" option const rewardType = mod.userConfig.rewardType || 'currency'; let selectedRewardType = rewardType; // If "both" is selected, randomly choose between currency and items if (rewardType === 'both') { selectedRewardType = Math.random() < 0.5 ? 'currency' : 'items'; } if (selectedRewardType === 'items') { const configuredItems = mod.userConfig.rewardItems || []; if (configuredItems.length > 0) { const randomItemIndex = Math.floor(Math.random() * configuredItems.length); const selectedItem = configuredItems[randomItemIndex]; await takaro.variable.variableControllerCreate({ key: 'trivia_reward', value: JSON.stringify({ type: 'items', itemId: selectedItem.item, amount: selectedItem.amount || 1, quality: selectedItem.quality || '' }), gameServerId, moduleId: mod.moduleId }); } else { console.error('Items reward type selected but no items configured, falling back to currency'); await takaro.variable.variableControllerCreate({ key: 'trivia_reward', value: JSON.stringify({ type: 'currency', amount: mod.userConfig.rewardAmount || 100 }), gameServerId, moduleId: mod.moduleId }); } } else { // Currency reward await takaro.variable.variableControllerCreate({ key: 'trivia_reward', value: JSON.stringify({ type: 'currency', amount: mod.userConfig.rewardAmount || 100 }), gameServerId, moduleId: mod.moduleId }); } const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value; // Create appropriate message based on question type let message = `🧠 Trivia Time! ${questionData.question}`; if (questionData.type === 'boolean') { message += ` (Answer with ${prefix}answer true or ${prefix}answer false)`; } else if (questionData.incorrectAnswers.length > 0) { const allAnswers = [questionData.answer, ...questionData.incorrectAnswers] .sort(() => Math.random() - 0.5); message += `\nOptions: ${allAnswers.join(', ')} (Answer with ${prefix}answer <your choice>)`; } else { message += ` (Answer with ${prefix}answer <your guess>)`; } // Send the message first await takaro.gameserver.gameServerControllerSendMessage(gameServerId, { message: message }); // Play trivia start sound to all online players const triviaStartSound = mod.userConfig.triviaStartSound; if (triviaStartSound) { await playSoundToPlayers(gameServerId, currentPlayersRes.data.data, triviaStartSound, 1); } console.log('Trivia question posted successfully'); } function decodeHtmlEntities(text) { if (!text) return text; const entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'", ''': "'", ' ': ' ', 'é': 'é', 'è': 'è', 'ê': 'ê', 'ë': 'ë', 'á': 'á', 'à': 'à', 'â': 'â', 'ä': 'ä', 'í': 'í', 'ì': 'ì', 'î': 'î', 'ï': 'ï', 'ó': 'ó', 'ò': 'ò', 'ô': 'ô', 'ö': 'ö', 'ú': 'ú', 'ù': 'ù', 'û': 'û', 'ü': 'ü', 'ñ': 'ñ', 'ç': 'ç' }; let decoded = text; for (const [entity, char] of Object.entries(entityMap)) { decoded = decoded.replace(new RegExp(entity, 'g'), char); } decoded = decoded.replace(/&#(\d+);/g, (match, num) => { return String.fromCharCode(parseInt(num, 10)); }); return decoded; } await main();
Permissions 2
Roles you can grant to decide who may use what.
-
Participate in Trivia
Allows players to participate in trivia games
-
Reset Trivia Leaderboard
Allows resetting the trivia leaderboard scores