Skip to content
This repository was archived by the owner on Mar 28, 2024. It is now read-only.

mc competitive completed to review with friends #54

Merged
merged 7 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "bot.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"babel-node": "babel-node --presets='@babel/preset-env' --ignore='foo|bar|baz'",
"babel-node": "babel-node --presets='@babel/preset-env'",
"start": "babel-node src/bot.js",
"dev": "nodemon --exec npm run babel-node -- src/bot.js"
},
Expand Down
222 changes: 221 additions & 1 deletion src/bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ bot.on('message', async (message) => {
// sends the embed to the channel
message.channel.send(embed);
}

if (command === 'play' && args[0] === 'mc' && args[1] === 'help' && args.length === 2) {
// command must be -play tf help, so that's what this conditional is looking for in order to successfully execute
const embed = new MessageEmbed(); // creates new embed instance

// setting the fields for the embed
embed
.setColor(0xffff00)
.setTitle('MC Modes')
.addField(
'`-play mc chill`',
'Initiates a round of 10 question Multiple Choice trivia with random difficulties and random categories. Its `chill` because this mode allows all users to attempt to answer within the 10 second time limit.'
)
.addField(
'`-play mc competitive`',
'Initiates a round of 10 question Multiple Choice trivia with random difficulties and random categories. Its `competitive` because this will only accept the first person that guesses correctly; everyone else loses by default. **TLDR; you have to be the first to answer correctly!**'
);
// sends the embed to the channel
message.channel.send(embed);
}

if (command === 'play' && args[0] === 'tf' && args[1] === 'chill' && args.length === 2) {
// command must be -play tf chill, so that's what this conditional is looking for in order to successfully execute

Expand Down Expand Up @@ -206,13 +227,212 @@ bot.on('message', async (message) => {
}
}

if (command === 'play' && args[0] === 'mc' && args[1] === 'competitive' && args.length === 2) {
// command must be -play mc competitive, so that's what this conditional is looking for in order to successfully execute

// sends a cute lil message to the channel letting the users know that a game will begin
message.channel.send('Lemme grab some questions for ya....');

/* creating empty trivia data variable for this round of trivia
It will be filled with data that was queried from the api, like so:
(the api sends it as an array of objects)

[
{
category: "Entertainment: Television",
type: "multiple",
difficulty: "medium",
question: "In the original Doctor Who series (1963), fourth doctor Tom Baker's scarf was how long?",
correct_answer: "7 Meters",
incorrect_answers: [
"10 Meters",
"2 Meters",
"5 Meters"
]
},
{
....so on and so forth....
}
]

Notice the data that the api sends back has more data than what we need; that's okay, we just won't use it
*/
let triviaData; // will hold the response that the api gave after a successful request

try {
// the api call is wrapped in a try/catch because it can fail, and we don't want our program to crash
triviaData = await (await axios(`https://opentdb.com/api.php?amount=10&type=multiple`)).data.results;
} catch (e) {
// if the api call does fail, we log the result and then send a cute lil error to the channel
console.log(e);
message.channel.send('Uh oh, something has gone wrong while trying to get some questions. Please try again');
}

const embed = new MessageEmbed(); // creates new embed instance
let counter = 10; // a counter that will help us execute the other channel messages later (helps us keep track of loop iterations)

/* instantiate empty leaderboard object where we'll store leaderboard stats
Takes the form:
{
elmo: 4,
bobthebuilder: 7,
....and so on and so forth....
}
*/
let leaderboard = {};

/* and now the fun begins.....
Loops over the contents of triviaData, and sends the question in an embed after the completion of the embed construction
*/
for (let i = 0; i < triviaData.length; i++) {
let choices = [`${triviaData[i].correct_answer}`]; // for testing, inputs the correct answer as the first choice for each question
for (let j = 0; j < 3; j++) { // adds the incorrect answers into the choices array created before
choices.push(`${triviaData[i].incorrect_answers[j]}`);
};
console.log(choices);

embed
.setTitle(`Question ${i + 1}`) // Title dynamically updates depending on which iteration we're on
.setColor(0xff0000) // color of the embed
.setDescription(
// the meat and potatoes of the embed
parseEntities(triviaData[i].question) + // the question
'\n' + // added a space
'\n**Choices**' + // added a space
'\n' +
'\n🇦 ' + parseEntities(choices[0]) + // outputs the choices from the array 'choices'
'\n🇧 ' + parseEntities(choices[1]) +
'\n🇨 ' + parseEntities(choices[2]) +
'\n🇩 ' + parseEntities(choices[3]) +
'\n' +
'\n**Difficulty:** ' + // putting double ** bolds the text, and single * italicizes it (in the Discord application)
parseEntities(triviaData[i].difficulty) + // difficulty
'\n**Category:** ' +
parseEntities(triviaData[i].category) // category
);

let msgEmbed = await message.channel.send(embed); // sends the embed
msgEmbed.react('🇦'); // adds a universal A emoji
msgEmbed.react('🇧'); // adds a universal B emoji
msgEmbed.react('🇨'); // and so on...
msgEmbed.react('🇩');
msgEmbed.react('🛑');

let answer = ''; // instantiate empty answer string, where correctAns will be housed

if (triviaData[i].correct_answer === choices[0]) {
// if the correct answer is the instance in the array, answer is equal to the corresponding letter emoji
answer = '🇦';
} else if (triviaData[i].correct_answer === choices[1]) {
// otherwise its incorrect emoji
answer = '🇧';
} else if (triviaData[i].correct_answer === choices[2]) {
// otherwise its incorrect emoji
answer = '🇨';
} else {
answer = '🇩';
}

// the createReactionCollector takes in a filter function, so we need to create the basis for what that filter is here
const filter = (reaction, user) => {
// filters only the reactions that are equal to the answer
return (reaction.emoji.name === answer || reaction.emoji.name === '🛑') && user.username !== bot.user.username;
};

// adds createReactionCollector to the embed we sent, so we can 'collect' all the correct answers
const collector = msgEmbed.createReactionCollector(filter, { max: 1, time: 10000 }); // will only collect for 10 seconds, and take one correct answer

// an array that will hold all the users that answered correctly
let usersWithCorrectAnswer = [];

// starts collecting
// r is reaction and user is user
collector.on('collect', (r, user) => {
// if the user is not the bot, and the reaction given is equal to the answer
// add the users that answered correctly to the usersWithCorrect Answer array
if (r.emoji.name === '🛑') {
counter = 0;
} else {
usersWithCorrectAnswer.push(user.username);
if (leaderboard[user.username] === undefined) {
// if the user isn't already in the leaderboard object, add them and give them a score of 1
leaderboard[user.username] = 1;
} else {
// otherwise, increment the user's score
leaderboard[user.username] += 1;
}
}
});
let newEmbed = new MessageEmbed(); // new embed instance

// what will be executed when the collector completes
collector.on('end', async () => {
// if no one got any answers right
if (usersWithCorrectAnswer.length === 0) {
// create an embed
let result = newEmbed.setTitle("Time's Up! No one got it....").setColor([168, 124, 124]);
// send the embed to the channel
message.channel.send(result);
} else {
// otherwise, create an embed with the results of the question
/* since the array is an array of strings, I used the javascript join() method to concat them, and then the replace() to replace the
comma with a comma and a space, so its human readable and pleasant to the eye
*/
let result = newEmbed
.setTitle("That's IT! Here's who is the first to get it right:")
.setDescription(usersWithCorrectAnswer.join().replace(',', ', '))
.setColor([168, 124, 124]);
// send the embed to the channel
message.channel.send(result);
}
});
/* if I don't include a pause of some sort, then the for loop will RAPID FIRE send all the questions to the channel
adding a pause here that is equal to the collection time (10 seconds) allows for time in between questions, and an
overall pleasant user experience
*/
await wait(10000);
// decrement the counter, tbh I don't know if having a counter is necessary now that I'm looking at this....we can fix this later
if (counter === 0) {
break;
}
counter--;
}
if (counter === 0) {
let winnerEmbed = new MessageEmbed(); // create new embed instance

// iterate over the leaderboard if winners exist (if the length of the object's keys isn't 0, then we have winners)
if (Object.keys(leaderboard).length !== 0) {
// specify the contents of the embed
let winner = winnerEmbed.setTitle('**Game Over!**').setDescription('**Final Scores: **').setColor([168, 124, 124]);

// loop over the contents of the leaderboard, and add fields to the embed on every iteration
for (const key in leaderboard) {
winner.addField(`${key}:`, `${leaderboard[key]}`);
}
message.channel.send(winner);
} else {
// if the leaderboard is empty, construct a different embed
winnerEmbed.setTitle('Game Over! No one got anything right...');
// send the embed to the channel
message.channel.send(winnerEmbed);
}
}
}

if (command === 'help') {
const embed = new MessageEmbed();
embed
.setColor(0xffff00)
.setTitle('How to use Trivia Bot')
.setDescription(
'**Useful Commands** \n`-help` Display all the commands \n`-play tf help` Gives more detail on the different modes in a T/F game \n`-play tf chill` Starts a round of chill T/F Trivia \n`-play tf competitive` Starts a round of competitive T/F Trivia \n`-stop` Terminate the Trivia Bot'
'**Useful Commands** \n`-help` Display all the commands \
\n`-play tf help` Gives more detail on the different modes in a T/F game \
\n`-play mc help` Gives more detail on the different modes in a Multiple Choice game \
\n`-play tf chill` Starts a round of chill T/F Trivia \
\n`-play mc chill` Starts a round of chill Multiple Choice Trivia \
\n`-play tf competitive` Starts a round of competitive T/F Trivia \
\n`-play mc competitive` Starts a round of competitive Multiple Choice Trivia \
\n`🛑` During the game, stop the game completely and tally the current totals by pressing this emoji reaction'
);
message.channel.send(embed);
}
Expand Down