Skip to content
Merged
37 changes: 36 additions & 1 deletion locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"config": {
"checking-config": "Konfiguration wird überprüft...",
"done-with-checking": "Konfiguration erfolgreich überprüft.",
"creating-file": "Konfiguration %m/%f exestiert aktuell nicht, wird aber gleich erstellt...",
"creating-file": "Konfiguration %m/%f existiert aktuell nicht, wird aber gleich erstellt...",
Comment thread
DEVTomatoCake marked this conversation as resolved.
"checking-of-field-failed": "Ein Fehler bei der Überprüfung von %fieldName in %m/%f ist aufgetreten",
"saved-file": "Konfiguration %f in %m wurde erfolgreich generiert.",
"moduleconf-regeneration": "Modul-Konfiguration wird regeniert, es werden keine Einstellungen überschrieben.",
Expand Down Expand Up @@ -926,5 +926,40 @@
"not-host": "Du bist nicht der Ersteller des Spiels!",
"max-players": "Das Spiel ist voll!",
"previous-cards": "Vorherige Karten: "
},
"quiz": {
"what-have-i-voted": "Was habe ich gewählt?",
"vote": "Abstimmen!",
"vote-this": "Wähle diese Option, wenn du denkst, dass diese richtig ist.",
"voted-successfully": "Erfolgreich ausgewählt. Danke für deine Teilnahme.",
"not-voted-yet": "Du hast noch keine Antwort ausgewählt, also kann ich dir nicht zeigen, für was du abgestimmt hast?",
"you-voted": "Du hast **%o** als Antwort ausgewählt.",
"change-opinion": "Du kannst deine Auswahl jederzeit ändern, indem du einfach etwas anderes über dem Knopf, den du gerade angeklickt hast, auswählst.",
"cannot-change-opinion": "Du kannst deine Auswahl nicht ändern, da der Ersteller diese Funktion deaktiviert hat.",
"select-correct": "Wähle alle richtigen Antworten aus",
"this-correct": "Diese Antwort als richtig markieren",
"cmd-description": "Erstelle oder spiele Quiz",
"cmd-create-normal-description": "Erstelle ein Quiz mit bis zu 10 Antworten",
"cmd-create-bool-description": "Erstelle ein Quiz, bei dem Nutzer nur Ja oder Nein auswählen können",
"cmd-play-description": "Spiele ein Server-Quiz",
"cmd-leaderboard-description": "Zeigt das Quiz-Leaderboard des Servers",
"cmd-create-description-description": "Thema / Beschreibung des Quiz",
"cmd-create-channel-description": "Kanal, in welchem dieses Quiz erstellt werden soll",
"cmd-create-endAt-description": "Relative Dauer des Quiz",
"cmd-create-option-description": "Option Nummer %o",
"cmd-create-canchange-description": "Ob die Teilnehmer ihre Auswahl nachträglich ändern können (Standard: Nein)",
"daily-quiz-limit": "Du hast das Limit von **%l** täglichen Quiz erreicht. Du kannst %timestamp wieder Quiz spielen.",
"created": "Quiz erfolgreich in %c erstellt.",
"correct-highlighted": "Alle richtigen Antworten wurden hervorgehoben.",
"answer-correct": "✅ Deine Antwort war richtig, du hast einen Punkt fürs Leaderboard erhalten!",
"answer-wrong": "❌ Deine Antwort war falsch!",
"bool-true": "Aussage stimmt",
"bool-false": "Aussage stimmt nicht",
"leaderboard-channel-not-found": "Der Leaderboard-Kanal wurde nicht gefunden oder sein Typ ist nicht erlaubt.",
"leaderboard-notation": "**%p. %u**: %xp XP",
"your-rank": "Du hast **%xp** Punkte in Quiz gesammelt!",
"no-rank": "Du hast noch nie ein Quiz erfolgreich beendet!",
"no-quiz": "Es wurden noch keine Quiz erstellt. Serveradmins können auf https://scnx.app/glink?page=bot/configuration?query=quiz&file=quiz%7Cconfigs%2FquizList Quiz hinzufügen.",
"no-permission": "Du hast keine Berechtigung, um Quiz mit dem Befehl zu erstellen."
}
}
35 changes: 35 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -910,5 +910,40 @@
"not-host": "You're not the host of the game!",
"max-players": "The game is full!",
"previous-cards": "Previous cards: "
},
"quiz": {
"what-have-i-voted": "What have I voted?",
"vote": "Vote!",
"vote-this": "Select this option if you think it's correct.",
"voted-successfully": "Selected successfully.",
"not-voted-yet": "You have not selected an option yet, so I cant show you what you selected?",
"you-voted": "You've selected **%o** as correct answer.",
"change-opinion": "You can change your opinion at any time by selecting another option above the button you just clicked.",
"cannot-change-opinion": "You cannot change your selection as the creator of this quiz disabled it.",
"select-correct": "Select all correct answers",
"this-correct": "Mark this answer as correct",
"cmd-description": "Create or play server quiz",
"cmd-create-normal-description": "Create a quiz with up to 10 answers",
"cmd-create-bool-description": "Create a quiz with true or false answers",
"cmd-play-description": "Play a server quiz",
"cmd-leaderboard-description": "Shows the quiz leaderboard of the server",
"cmd-create-description-description": "Title / description of the quiz",
"cmd-create-channel-description": "Channel in which the quiz should be created",
"cmd-create-endAt-description": "How long the quiz will last",
"cmd-create-option-description": "Option number %o",
"cmd-create-canchange-description": "If the players can change their opinion after voting (default: no)",
"daily-quiz-limit": "You've reached the limit of **%l** daily playable quizzes. You can play again %timestamp.",
"created": "Quiz created successfully in %c.",
"correct-highlighted": "All correct answers were highlighted.",
"answer-correct": "✅ Your answer was correct and you've received one point for the leaderboard!",
"answer-wrong": "❌ Your answer was wrong!",
"bool-true": "Statement is correct",
"bool-false": "Statement is wrong",
"leaderboard-channel-not-found": "The leaderboard channel couldn't be found or it's type is invalid.",
"leaderboard-notation": "**%p. %u**: %xp XP",
"your-rank": "You've collected **%xp** points in quiz!",
"no-rank": "You've never finished a quiz successfully!",
"no-quiz": "No quizzes have been created for this server. Trusted admins can create them on https://scnx.app/glink?page=bot/configuration?query=quiz&file=quiz%7Cconfigs%2FquizList .",
"no-permission": "You don't have enough permissions to create quiz using the command."
}
}
260 changes: 260 additions & 0 deletions modules/quiz/commands/quiz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
const {MessageEmbed} = require('discord.js');
const durationParser = require('parse-duration');
const { formatDate } = require('../../../src/functions/helpers');
const {localize} = require('../../../src/functions/localize');
const {createQuiz} = require('../quizUtil');

/**
* Handles quiz create commands
* @param {Discord.ApplicationCommandInteraction} interaction
*/
async function create(interaction) {
const config = interaction.client.configurations['quiz']['config'];
if (!interaction.member.roles.cache.has(config.createAllowedRole)) return interaction.reply({content: localize('quiz', 'no-permission'), ephemeral: true});

let endAt;
let options = [];
let emojis = config.emojis;
if (interaction.options.getSubcommand() === 'create-bool') {
options = [{text: localize('quiz', 'bool-true')}, {text: localize('quiz', 'bool-false')}];
emojis = [null, emojis.true, emojis.false];
} else {
for (let step = 1; step <= 10; step++) {
if (interaction.options.getString('option' + step)) options.push({text: interaction.options.getString('option' + step)});
}
}

const selectOptions = [];
for (const vId in options) {
selectOptions.push({
label: options[vId].text,
value: vId,
description: localize('quiz', 'this-correct'),
emoji: emojis[parseInt(vId) + 1]
});
}
const msg = await interaction.reply({
components: [{
type: 'ACTION_ROW',
components: [{
/* eslint-disable camelcase */
type: 'SELECT_MENU',
custom_id: 'quiz',
placeholder: localize('quiz', 'select-correct'),
min_values: 1,
max_values: interaction.options.getSubcommand() === 'create-bool' ? 1 : options.length,
options: selectOptions
}]
}],
ephemeral: true,
fetchReply: true
});
const collector = msg.createMessageComponentCollector({filter: i => interaction.user.id === i.user.id, componentType: 'SELECT_MENU', max: 1});
collector.on('collect', async i => {
i.values.forEach(option => {
options[option].correct = true;
});

if (interaction.options.getString('duration')) endAt = new Date(new Date().getTime() + durationParser(interaction.options.getString('duration')));
await createQuiz({
description: interaction.options.getString('description', true),
channel: interaction.options.getChannel('channel', true),
endAt,
options,
canChangeVote: interaction.options.getBoolean('canchange') || false,
type: interaction.options.getSubcommand() === 'create-bool' ? 'bool' : 'normal'
}, interaction.client);
i.update({
content: localize('quiz', 'created', {c: interaction.options.getChannel('channel').toString()}),
components: []
});
});
}

module.exports.subcommands = {
'create': create,
'create-bool': create,
'play': async function (interaction) {
let user = await interaction.client.models['quiz']['QuizUser'].findAll({where: {userId: interaction.user.id}});
if (user.length > 0) user = user[0];
else user = await interaction.client.models['quiz']['QuizUser'].create({userID: interaction.user.id, dailyQuiz: 0});

if (user.dailyQuiz >= interaction.client.configurations['quiz']['config'].dailyQuizLimit) {
const now = new Date();
now.setDate(now.getDate() + 1);
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);

return interaction.reply({
content: localize('quiz', 'daily-quiz-limit', {l: interaction.client.configurations['quiz']['config'].dailyQuizLimit, timestamp: formatDate(now)}),
ephemeral: true
});
}
if (!interaction.client.configurations['quiz']['quizList'] || interaction.client.configurations['quiz']['quizList'].length === 0) return interaction.reply({content: localize('quiz', 'no-quiz'), ephemeral: true});

const updatedUser = {dailyQuiz: user.dailyQuiz + 1};
let quiz = {};
if (interaction.client.configurations['quiz']['config'].mode.toLowerCase() === 'continuous') {
quiz = interaction.client.configurations['quiz']['quizList'][user.nextQuizID] || interaction.client.configurations['quiz']['quizList'][0];
updatedUser.nextQuizID = interaction.client.configurations['quiz']['quizList'][user.nextQuizID + 1] ? user.nextQuizID + 1 : 0;
} else quiz = interaction.client.configurations['quiz']['quizList'][Math.floor(Math.random() * interaction.client.configurations['quiz']['quizList'].length)];

quiz.channel = interaction.channel;
quiz.options = [
...quiz.wrongOptions.map(o => ({text: o})),
...quiz.correctOptions.map(o => ({text: o, correct: true}))
];
quiz.endAt = new Date(new Date().getTime() + durationParser(quiz.duration));
quiz.canChangeVote = false;
quiz.private = true;
createQuiz(quiz, interaction.client, interaction);

interaction.client.models['quiz']['QuizUser'].update(updatedUser, {where: {userID: interaction.user.id}});
},
'leaderboard': async function (interaction) {
const moduleStrings = interaction.client.configurations['quiz']['strings'];
const users = await interaction.client.models['quiz']['QuizUser'].findAll({
order: [
['xp', 'DESC']
],
limit: 15
});

let leaderboardString = '';
let i = 0;
for (const user of users) {
const member = interaction.guild.members.cache.get(user.userID);
if (!member) continue;
i++;
leaderboardString = leaderboardString + localize('quiz', 'leaderboard-notation', {
p: i,
u: member.user.toString(),
xp: user.xp
}) + '\n';
}
if (leaderboardString.length === 0) leaderboardString = localize('levels', 'no-user-on-leaderboard');

const embed = new MessageEmbed()
.setTitle(moduleStrings.embed.leaderboardTitle)
.setColor(moduleStrings.embed.leaderboardColor)
.setFooter({text: interaction.client.strings.footer, iconURL: interaction.client.strings.footerImgUrl})
.setThumbnail(interaction.guild.iconURL())
.addField(moduleStrings.embed.leaderboardSubtitle, leaderboardString);

if (!interaction.client.strings.disableFooterTimestamp) embed.setTimestamp();

const components = [{
type: 'ACTION_ROW',
components: [{
type: 'BUTTON',
label: moduleStrings.embed.leaderboardButton,
style: 'SUCCESS',
customId: 'show-quiz-rank'
}]
}];

interaction.reply({embeds: [embed], components});
}
};

module.exports.config = {
name: 'quiz',
description: localize('quiz', 'cmd-description'),
defaultPermission: false,
options: function () {
const options = [
{
type: 'SUB_COMMAND',
name: 'create',
description: localize('quiz', 'cmd-create-normal-description'),
options: [{
type: 'STRING',
name: 'description',
required: true,
description: localize('quiz', 'cmd-create-description-description')
},
{
type: 'CHANNEL',
name: 'channel',
required: true,
channelTypes: ['GUILD_TEXT', 'GUILD_NEWS', 'GUILD_VOICE'],
description: localize('quiz', 'cmd-create-channel-description')
},
{
type: 'STRING',
name: 'duration',
required: true,
description: localize('quiz', 'cmd-create-endAt-description')
},
{
type: 'STRING',
name: 'option1',
required: true,
description: localize('quiz', 'cmd-create-option-description', {o: 1})
},
{
type: 'STRING',
name: 'option2',
required: true,
description: localize('quiz', 'cmd-create-option-description', {o: 2})
},
{
type: 'BOOLEAN',
name: 'canchange',
required: false,
description: localize('quiz', 'cmd-create-canchange-description')
}]
},
{
type: 'SUB_COMMAND',
name: 'create-bool',
description: localize('quiz', 'cmd-create-bool-description'),
options: [{
type: 'STRING',
name: 'description',
required: true,
description: localize('quiz', 'cmd-create-description-description')
},
{
type: 'CHANNEL',
name: 'channel',
required: true,
channelTypes: ['GUILD_TEXT', 'GUILD_NEWS', 'GUILD_VOICE'],
description: localize('quiz', 'cmd-create-channel-description')
},
{
type: 'BOOLEAN',
name: 'canchange',
required: false,
description: localize('quiz', 'cmd-create-canchange-description')
},
{
type: 'STRING',
name: 'duration',
required: false,
description: localize('quiz', 'cmd-create-endAt-description')
}]
},
{
type: 'SUB_COMMAND',
name: 'play',
description: localize('quiz', 'cmd-play-description')
},
{
type: 'SUB_COMMAND',
name: 'leaderboard',
description: localize('quiz', 'cmd-leaderboard-description')
}
];
for (let step = 1; step <= 7; step++) {
options[0].options.push({
type: 'STRING',
name: `option${2 + step}`,
required: false,
description: localize('quiz', 'cmd-create-option-description', {o: 2 + step})
});
}
return options;
}
};
Loading