Browse Source

Merge pull request #25 from ADawesomeguy/refactor

Refactor to make contribution and maintenance easier
pull/28/head
Abheek Dhawan 3 years ago
committed by GitHub
parent
commit
42db71d60e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 76
      commands/about.js
  2. 14
      commands/help.js
  3. 110
      commands/rounds.js
  4. 39
      commands/top.js
  5. 169
      commands/train.js
  6. 20
      deploy-commands.js
  7. 20
      events/interactionCreate.js
  8. 11
      events/ready.js
  9. 41
      helpers/db.js
  10. 8
      helpers/env.js
  11. 440
      index.js
  12. 1
      package.json
  13. 137
      slashCommands.json
  14. 22
      yarn.lock

76
commands/about.js

@ -0,0 +1,76 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageEmbed } = require('discord.js');
const gitlog = require('gitlog').default;
const userScore = require('../models/userScore');
module.exports = {
data: new SlashCommandBuilder()
.setName('about')
.setDescription('Commands regarding the creation/development of the bot')
.addSubcommand(subcommand => {
subcommand
.setName('contributors')
.setDescription('Lists contributors to the AwesomeSciBo bot');
return subcommand;
})
.addSubcommand(subcommand => {
subcommand
.setName('changelog')
.setDescription('Lists the 5 most recent changes in a "git log" type format');
return subcommand;
})
.addSubcommand(subcommand => {
subcommand
.setName('bot')
.setDescription('Lists information about AwesomeSciBo');
return subcommand;
}),
async execute(interaction) {
const client = interaction.client;
const action = interaction.options.getSubcommand();
if (action === 'contributors') {
const contributorEmbed = new MessageEmbed().setTitle('Contributors')
.addField('Creator', '<@745063586422063214> [ADawesomeguy#3602]', true)
.addField('Contributors', '<@650525101048987649> [tEjAs#8127]\n<@426864344463048705> [tetrident#9396]', true) // Add more contributors here, first one is Abheek, second one is Tejas
.setTimestamp()
.setColor('#ffffff');
interaction.reply({ embeds: [contributorEmbed] });
}
else if (action === 'changelog') {
const gitRepoLocation = __dirname;
const commits = gitlog({
repo: gitRepoLocation,
number: 5,
fields: ['hash', 'abbrevHash', 'subject', 'authorName', 'authorDateRel'],
});
const changelogEmbed = new MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.setTitle('Changelog')
.setColor('#ffffff')
.setTimestamp();
commits.forEach(commit => {
changelogEmbed.addField(commit.abbrevHash, `> \`Hash:\`${commit.hash}\n> \`Subject:\`${commit.subject}\n> \`Author:\`${commit.authorName}\n> \`Date:\`${commit.authorDateRel}\n> \`Link\`: [GitHub](https://github.com/ADawesomeguy/AwesomeSciBo/commit/${commit.hash})\n`);
});
interaction.reply({ embeds: [changelogEmbed] });
}
else if (action === 'bot') {
await client.guilds.fetch();
const trainingDocuments = await userScore.countDocuments({});
const aboutBotEmbed = new MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.setTitle('About AwesomeSciBo')
.addField('Servers', `${client.guilds.cache.size}`, true)
.addField('Training Users', `${trainingDocuments}`, true)
.setTimestamp();
interaction.reply({ embeds: [aboutBotEmbed] });
}
},
};

14
commands/help.js

@ -0,0 +1,14 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageEmbed } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('help')
.setDescription('Replies with a help message explaining what the bot can do'),
async execute(interaction) {
const helpEmbed = new MessageEmbed()
.setDescription('AwesomeSciBo has migrated to using slash commands! You can take a look at the different commands by typing `/` and clicking on the AwesomeSciBo icon.')
.setColor('#ffffff');
interaction.reply({ embeds: [helpEmbed] });
},
};

110
commands/rounds.js

@ -0,0 +1,110 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageEmbed } = require('discord.js');
const axios = require('axios');
const generatedRound = require('../models/generateRound');
module.exports = {
data: new SlashCommandBuilder()
.setName('rounds')
.setDescription('Commands regarding the generation of rounds')
.addSubcommand(subcommand => {
subcommand
.setName('generate')
.setDescription('Generates a round with randomized questions from https://scibowldb.com/');
return subcommand;
})
.addSubcommand(subcommand => {
subcommand
.setName('list')
.setDescription('Lists your 5 most recently generated rounds with links');
return subcommand;
})
.addSubcommand(subcommand => {
subcommand
.setName('hit')
.setDescription('Shows the total number of rounds hit as well as the number for the specific user');
return subcommand;
}),
async execute(interaction) {
const action = interaction.options.getSubcommand();
if (action === 'generate') {
let i;
let finalizedHTML = '<html><head><link rel=\'preconnect\' href=\'https://fonts.gstatic.com\'><link href=\'https://fonts.googleapis.com/css2?family=Ubuntu&display=swap\' rel=\'stylesheet\'> </head><body style=\'width: 70%; margin-left: auto; margin-right: auto;\'><h2 style=\'text-align: center; text-decoration: underline overline; padding: 7px;\'>ROUND GENERATED BY AWESOMESCIBO USING THE SCIBOWLDB API</h2>';
let tossup_question;
let question_category;
let tossup_format;
let tossup_answer;
let bonus_question;
let bonus_format;
let bonus_answer;
let htmlContent = '';
await axios.post('https://scibowldb.com/api/questions', { categories: ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'] })
.then((response) => {
for (i = 1; i < 26; i++) {
const data = response.data.questions[Math.floor(Math.random() * response.data.questions.length)];
tossup_question = data.tossup_question;
tossup_answer = data.tossup_answer;
question_category = data.category;
tossup_format = data.tossup_format;
bonus_question = data.bonus_question;
bonus_answer = data.bonus_answer;
bonus_format = data.bonus_format;
htmlContent = '<br><br><h3 style=\'text-align: center;\'><strong>TOSS-UP</strong></h3>\n<br>' + `${i}) <strong>${question_category}</strong>` + ' ' + `<em>${tossup_format}</em>` + ' ' + tossup_question + '<br><br>' + '<strong>ANSWER:</strong> ' + tossup_answer + '<br>';
htmlContent += '<br><br><h3 style=\'text-align: center;\'><strong>BONUS</strong></h3>\n<br>' + `${i}) <strong>${question_category}</strong>` + ' ' + `<em>${bonus_format}</em>` + ' ' + bonus_question + '<br><br>' + '<strong>ANSWER:</strong> ' + bonus_answer + '<br><br><hr><br>';
htmlContent = htmlContent.replace(/\n/g, '<br>');
finalizedHTML += htmlContent;
}
const newGeneratedRound = new generatedRound({
htmlContent: finalizedHTML,
requestedBy: interaction.user.id,
authorTag: interaction.user.tag,
timestamp: new Date().toISOString(),
});
newGeneratedRound.save((err, round) => {
if (err) {
console.log(err);
return;
}
interaction.reply(`Here's your round: https://api.adawesome.tech/round/${round._id.toString()}`, { ephemeral: true });
});
});
}
else if (action === 'list') {
let roundsList = await generatedRound.find({ requestedBy: interaction.user.id }).sort({ timestamp: -1 });
let finalMessage = '';
if (!roundsList) {
interaction.reply('You haven\'t requested any roundsList!');
return;
}
if (roundsList.length > 5) {
roundsList = roundsList.slice(0, 5);
}
roundsList.forEach(async (item, index) => {
finalMessage += `${index + 1}. [${item.timestamp.split('T')[0]}](https://api.adawesome.tech/round/${item._id.toString()})\n`;
});
const roundsListEmbed = new MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.setTitle(`Last 5 roundsList requested by ${interaction.user.tag}`)
.setDescription(finalMessage)
.setTimestamp();
interaction.reply({
embeds: [roundsListEmbed],
ephemeral: true,
});
}
else if (action === 'hit') {
const totalCount = await generatedRound.countDocuments({});
const userCount = await generatedRound.countDocuments({ requestedBy: interaction.user.id });
interaction.reply(`Total Hits: ${totalCount}\nYour Hits: ${userCount}`);
}
},
};

39
commands/top.js

@ -0,0 +1,39 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageEmbed } = require('discord.js');
const userScore = require('../models/userScore');
module.exports = {
data: new SlashCommandBuilder()
.setName('top')
.setDescription('Lists top ten scores across servers (server specific leaderboard WIP)'),
async execute(interaction) {
let messageContent = '';
userScore
.find({})
.sort({ score: -1 }) // Sort by descending order
.exec((err, obj) => {
if (err) {
console.log(err);
return interaction.reply(
'Uh oh! :( There was an internal error. Please try again.',
);
}
if (obj.length < 10) {
// Need at least 10 scores for top 10
return interaction.reply(
`There are only ${obj.length} users, we need at least 10!`,
);
}
for (let i = 0; i < 10; i++) {
messageContent += `${i + 1}: <@${obj[i].authorID}>: ${obj[i].score}\n`; // Loop through each user and add their name and score to leaderboard content
}
const leaderboardEmbed = new MessageEmbed()
.setTitle('Top Ten!')
.setDescription(messageContent)
.setColor('#ffffff');
interaction.reply({ embeds: [leaderboardEmbed] });
});
},
};

169
commands/train.js

@ -0,0 +1,169 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageEmbed } = require('discord.js');
const decode = require('html-entities').decode;
const axios = require('axios');
const userScore = require('../models/userScore');
const { updateScore } = require('../helpers/db.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('train')
.setDescription('Sends a training question to be answered')
.addStringOption(option => {
option
.setName('subject')
.setDescription('Optional subject to be used as a filter')
.setRequired(false)
.addChoice('astro', 'astro')
.addChoice('bio', 'bio')
.addChoice('ess', 'ess')
.addChoice('chem', 'chem')
.addChoice('phys', 'phys')
.addChoice('math', 'math')
.addChoice('energy', 'energy')
.setRequired(false);
return option;
}),
async execute(interaction) {
const subject = interaction.options.get('subject') ? interaction.options.get('subject').value : null;
const authorId = interaction.user.id;
let score;
userScore
.findOne({ authorID: authorId })
.lean()
.then((obj, err) => {
if (!obj) {
score = 0;
}
else if (obj) {
score = obj.score;
}
else {
console.log(err);
}
});
let categoryArray = [];
switch (subject) {
case null:
categoryArray = ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'];
break;
case 'astro':
case 'astronomy':
categoryArray = ['ASTRONOMY'];
break;
case 'bio':
case 'biology':
categoryArray = ['BIOLOGY'];
break;
case 'ess':
case 'earth science':
case 'es':
categoryArray = ['EARTH SCIENCE'];
break;
case 'chem':
case 'chemistry':
categoryArray = ['CHEMISTRY'];
break;
case 'phys':
case 'physics':
categoryArray = ['PHYSICS'];
break;
case 'math':
categoryArray = ['MATH'];
break;
case 'energy':
categoryArray = ['ENERGY'];
break;
default:
interaction.reply(
new MessageEmbed()
.setDescription('<:red_x:816791117671825409> Not a valid subject!')
.setColor('#ffffff'),
);
return;
}
axios
.post('https://scibowldb.com/api/questions/random', { categories: categoryArray })
.then((res) => {
const data = res.data.question;
const tossupQuestion = data.tossup_question;
const tossupAnswer = data.tossup_answer;
const messageFilter = message => message.author.id === interaction.author.id;
interaction.reply({ content: decode(tossupQuestion) + `\n\n||Source: ${data.uri}||` })
.then(() => {
interaction.channel.awaitMessages({
messageFilter,
max: 1,
})
.then(collected => {
console.log(collected.first());
const answerMsg = collected.first();
let predicted = null;
if (data.tossup_format === 'Multiple Choice') {
if (
answerMsg.content.charAt(0).toLowerCase() ===
tossupAnswer.charAt(0).toLowerCase()
) {
predicted = 'correct';
}
else {
predicted = 'incorrect';
}
}
else if (
answerMsg.content.toLowerCase() ===
tossupAnswer.toLowerCase()
) {
predicted = 'correct';
}
else {
predicted = 'incorrect';
}
if (predicted === 'correct') {
updateScore(true, score, authorId).then((msgToReply) =>
answerMsg.reply(msgToReply),
);
}
else {
const overrideEmbed = new MessageEmbed()
.setAuthor({ name: answerMsg.author.tag, iconURL: answerMsg.author.displayAvatarURL() })
.addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription('It seems your answer was incorrect. Please react with <:override:842778128966615060> to override your answer if you think you got it right.')
.setColor('#ffffff')
.setTimestamp();
answerMsg.channel.send({
embeds: [overrideEmbed],
})
.then(overrideMsg => {
overrideMsg.react('<:override:842778128966615060>');
const filter = (reaction, user) => {
return (
['override'].includes(reaction.emoji.name) &&
user.id === answerMsg.author.id
);
};
overrideMsg
.awaitReactions({
filter,
max: 1,
})
.then(() => {
updateScore(true, score, authorId).then((msgToReply) =>
answerMsg.reply(msgToReply),
);
}).catch(console.error);
}).catch(console.error);
}
}).catch(console.error);
}).catch(console.error);
}).catch(console.error);
},
};

20
deploy-commands.js

@ -0,0 +1,20 @@
#!/usr/bin/env node
const fs = require('node:fs');
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const { clientId, token } = require('./helpers/env');
const commands = [];
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands.push(command.data.toJSON());
}
const rest = new REST({ version: '9' }).setToken(token);
rest.put(Routes.applicationCommands(clientId), { body: commands })
.then(() => console.log('Successfully registered application commands.'))
.catch(console.error);

20
events/interactionCreate.js

@ -0,0 +1,20 @@
module.exports = {
name: 'interactionCreate',
once: false,
async execute(interaction) {
const client = interaction.client;
if (!interaction.isCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
}
catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
},
};

11
events/ready.js

@ -0,0 +1,11 @@
const db = require('../helpers/db');
const { mongoUri } = require('../helpers/env');
module.exports = {
name: 'ready',
once: true,
async execute(client) {
await db.connect(mongoUri);
console.log(`Logged in at ${client.user.tag}!`);
},
};

41
helpers/db.js

@ -0,0 +1,41 @@
const mongoose = require('mongoose');
const userScore = require('../models/userScore');
module.exports = {
async updateScore(isCorrect, score, authorId) {
if (!isCorrect) {
return `Nice try! Your score is still ${score}.`;
}
else {
score += 4;
if (score == 4) {
const newUserScore = new userScore({
authorID: authorId,
score: score,
});
newUserScore.save((err) =>
err
? console.log('Error creating new user for scoring')
: console.log('Sucessfully created user to score.'),
);
}
else {
const doc = await userScore.findOne({
authorID: authorId,
});
doc.score = doc.score + 4;
doc.save();
}
return `Great job! Your score is now ${score}.`;
}
},
async connect(mongoUri) {
mongoose
.connect(mongoUri, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
},
};

8
helpers/env.js

@ -0,0 +1,8 @@
require('dotenv').config();
module.exports = {
clientId: process.env.CLIENT_ID,
testingGuild: process.env.TESTING_GUILD,
token: process.env.TOKEN,
mongoUri: process.env.MONGO_URI,
};

440
index.js

@ -1,437 +1,31 @@
#!/usr/bin/env node #!/usr/bin/env node
require('dotenv').config(); const fs = require('node:fs');
const { Client, Collection, Intents } = require('discord.js');
const { token } = require('./helpers/env');
const Discord = require('discord.js'); const client = new Client({
const client = new Discord.Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS, Intents.FLAGS.DIRECT_MESSAGES, Intents.FLAGS.DIRECT_MESSAGE_REACTIONS],
intents: ['GUILDS', 'GUILD_MESSAGES', 'GUILD_MESSAGE_REACTIONS', 'DIRECT_MESSAGES', 'DIRECT_MESSAGE_REACTIONS'],
partials: ['MESSAGE', 'CHANNEL', 'REACTION'],
}); });
const axios = require('axios');
const mongoose = require('mongoose');
const gitlog = require('gitlog').default;
const decode = require('html-entities').decode;
const userScore = require('./models/userScore'); client.commands = new Collection();
const generatedRound = require('./models/generateRound');
const helpMessage = 'AwesomeSciBo has migrated to using slash commands! You can take a look at the different commands by typing `/` and clicking on the AwesomeSciBo icon.'; const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
const slashCommands = require('./slashCommands.json'); const eventFiles = fs.readdirSync('./events').filter(file => file.endsWith('.js'));
const config = {
topggauth: process.env['TOPGGAUTH'],
};
client.once('ready', () => { for (const file of commandFiles) {
client.application.commands.set(slashCommands); const command = require(`./commands/${file}`);
client.commands.set(command.data.name, command);
// Connect to MongoDB using mongoose
if (!process.env.CI) {
mongoose
.connect(process.env.MONGO_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
})
.then(() => {
// Log client tag and set status
console.log(`Logged in as: ${client.user.username}!`);
client.user.setActivity(
'for /help',
{ type: 'WATCHING' },
);
})
.catch((err) => console.log(err));
}
});
client.on('guildCreate', () => {
const topggAuthHeader = {
headers: {
'Authorization': config.topggauth,
},
};
axios.post(`https://top.gg/api/bots/${client.user.id}/stats`, { server_count: client.guilds.cache.size }, topggAuthHeader).then(response => { console.log(response); });
});
client.on('guildDelete', () => {
const topggAuthHeader = {
headers: {
'Authorization': config.topggauth,
},
};
axios.post(`https://top.gg/api/bots/${client.user.id}/stats`, { server_count: client.guilds.cache.size }, topggAuthHeader);
});
async function updateScore(isCorrect, score, authorId) {
if (!isCorrect) {
return `Nice try! Your score is still ${score}.`;
}
else {
score += 4;
if (score == 4) {
const newUserScore = new userScore({
authorID: authorId,
score: score,
});
newUserScore.save((err) =>
err
? console.log('Error creating new user for scoring')
: console.log('Sucessfully created user to score.'),
);
}
else {
const doc = await userScore.findOne({
authorID: authorId,
});
doc.score = doc.score + 4;
doc.save();
}
return `Great job! Your score is now ${score}.`;
}
} }
async function training(subject, interaction) { for (const file of eventFiles) {
const authorId = interaction.user.id; const event = require(`./events/${file}`);
let score; if (event.once) {
userScore client.once(event.name, (...args) => event.execute(...args));
.findOne({ authorID: authorId })
.lean()
.then((obj, err) => {
if (!obj) {
score = 0;
}
else if (obj) {
score = obj.score;
} }
else { else {
console.log(err); client.on(event.name, (...args) => event.execute(...args));
}
});
let categoryArray = [];
switch (subject) {
case null:
categoryArray = ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'];
break;
case 'astro':
case 'astronomy':
categoryArray = ['ASTRONOMY'];
break;
case 'bio':
case 'biology':
categoryArray = ['BIOLOGY'];
break;
case 'ess':
case 'earth science':
case 'es':
categoryArray = ['EARTH SCIENCE'];
break;
case 'chem':
case 'chemistry':
categoryArray = ['CHEMISTRY'];
break;
case 'phys':
case 'physics':
categoryArray = ['PHYSICS'];
break;
case 'math':
categoryArray = ['MATH'];
break;
case 'energy':
categoryArray = ['ENERGY'];
break;
default:
interaction.reply(
new Discord.MessageEmbed()
.setDescription('<:red_x:816791117671825409> Not a valid subject!')
.setColor('#ffffff'),
);
return;
}
axios
.post('https://scibowldb.com/api/questions/random', { categories: categoryArray })
.then((res) => {
const data = res.data.question;
const tossupQuestion = data.tossup_question;
const tossupAnswer = data.tossup_answer;
const messageFilter = message => message.author.id === interaction.author.id;
interaction.reply({ content: decode(tossupQuestion) + `\n\n||Source: ${data.uri}||` })
.then(() => {
interaction.channel.awaitMessages({
messageFilter,
max: 1,
})
.then(collected => {
console.log(collected.first());
const answerMsg = collected.first();
let predicted = null;
if (data.tossup_format === 'Multiple Choice') {
if (
answerMsg.content.charAt(0).toLowerCase() ===
tossupAnswer.charAt(0).toLowerCase()
) {
predicted = 'correct';
}
else {
predicted = 'incorrect';
}
}
else if (
answerMsg.content.toLowerCase() ===
tossupAnswer.toLowerCase()
) {
predicted = 'correct';
}
else {
predicted = 'incorrect';
}
if (predicted === 'correct') {
updateScore(true, score, authorId).then((msgToReply) =>
answerMsg.reply(msgToReply),
);
}
else {
const overrideEmbed = new Discord.MessageEmbed()
.setAuthor({ name: answerMsg.author.tag, iconURL: answerMsg.author.displayAvatarURL() })
.addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription('It seems your answer was incorrect. Please react with <:override:842778128966615060> to override your answer if you think you got it right.')
.setColor('#ffffff')
.setTimestamp();
answerMsg.channel.send({
embeds: [overrideEmbed],
})
.then(overrideMsg => {
overrideMsg.react('<:override:842778128966615060>');
const filter = (reaction, user) => {
return (
['override'].includes(reaction.emoji.name) &&
user.id === answerMsg.author.id
);
};
overrideMsg
.awaitReactions({
filter,
max: 1,
})
.then(() => {
updateScore(true, score, authorId).then((msgToReply) =>
answerMsg.reply(msgToReply),
);
}).catch(console.error);
}).catch(console.error);
}
}).catch(console.error);
}).catch(console.error);
}).catch(console.error);
}
function sendHelpMessage(interaction) {
const helpEmbed = new Discord.MessageEmbed().setDescription(helpMessage).setColor('ffffff');
interaction.reply({ embeds: [helpEmbed] });
}
function showLeaderboard(interaction) {
let messageContent = '';
userScore
.find({})
.sort({ score: -1 }) // Sort by descending order
.exec((err, obj) => {
if (err) {
console.log(err);
return interaction.reply(
'Uh oh! :( There was an internal error. Please try again.',
);
}
if (obj.length < 10) {
// Need at least 10 scores for top 10
return interaction.reply(
`There are only ${obj.length} users, we need at least 10!`,
);
}
for (let i = 0; i < 10; i++) {
messageContent += `${i + 1}: <@${obj[i].authorID}>: ${obj[i].score}\n`; // Loop through each user and add their name and score to leaderboard content
}
const leaderboardEmbed = new Discord.MessageEmbed()
.setTitle('Top Ten!')
.setDescription(messageContent)
.setColor('#ffffff');
interaction.reply({ embeds: [leaderboardEmbed] });
});
}
async function about(action, interaction) {
if (action === 'contributors') {
const contributorEmbed = new Discord.MessageEmbed().setTitle('Contributors')
.addField('Creator', '<@745063586422063214> [ADawesomeguy#3602]', true)
.addField('Contributors', '<@650525101048987649> [tEjAs#8127]\n<@426864344463048705> [tetrident#9396]', true) // Add more contributors here, first one is Abheek, second one is Tejas
.setTimestamp()
.setColor('#ffffff');
interaction.reply({ embeds: [contributorEmbed] });
}
else if (action === 'changelog') {
const gitRepoLocation = __dirname;
const commits = gitlog({
repo: gitRepoLocation,
number: 5,
fields: ['hash', 'abbrevHash', 'subject', 'authorName', 'authorDateRel'],
});
const changelogEmbed = new Discord.MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.setTitle('Changelog')
.setColor('#ffffff')
.setTimestamp();
commits.forEach(commit => {
changelogEmbed.addField(commit.abbrevHash, `> \`Hash:\`${commit.hash}\n> \`Subject:\`${commit.subject}\n> \`Author:\`${commit.authorName}\n> \`Date:\`${commit.authorDateRel}\n> \`Link\`: [GitHub](https://github.com/ADawesomeguy/AwesomeSciBo/commit/${commit.hash})\n`);
});
interaction.reply({ embeds: [changelogEmbed] });
}
else if (action === 'bot') {
await client.guilds.fetch();
const trainingDocuments = await userScore.countDocuments({});
const aboutBotEmbed = new Discord.MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.setTitle('About AwesomeSciBo')
.addField('Servers', `${client.guilds.cache.size}`, true)
.addField('Training Users', `${trainingDocuments}`, true)
.setTimestamp();
interaction.reply({ embeds: [aboutBotEmbed] });
} }
} }
async function rounds(action, interaction) { client.login(token);
if (action === 'generate') {
let i;
let finalizedHTML = '<html><head><link rel=\'preconnect\' href=\'https://fonts.gstatic.com\'><link href=\'https://fonts.googleapis.com/css2?family=Ubuntu&display=swap\' rel=\'stylesheet\'> </head><body style=\'width: 70%; margin-left: auto; margin-right: auto;\'><h2 style=\'text-align: center; text-decoration: underline overline; padding: 7px;\'>ROUND GENERATED BY AWESOMESCIBO USING THE SCIBOWLDB API</h2>';
let tossup_question;
let question_category;
let tossup_format;
let tossup_answer;
let bonus_question;
let bonus_format;
let bonus_answer;
let htmlContent = '';
await axios.post('https://scibowldb.com/api/questions', { categories: ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'] })
.then((response) => {
for (i = 1; i < 26; i++) {
const data = response.data.questions[Math.floor(Math.random() * response.data.questions.length)];
tossup_question = data.tossup_question;
tossup_answer = data.tossup_answer;
question_category = data.category;
tossup_format = data.tossup_format;
bonus_question = data.bonus_question;
bonus_answer = data.bonus_answer;
bonus_format = data.bonus_format;
htmlContent = '<br><br><h3 style=\'text-align: center;\'><strong>TOSS-UP</strong></h3>\n<br>' + `${i}) <strong>${question_category}</strong>` + ' ' + `<em>${tossup_format}</em>` + ' ' + tossup_question + '<br><br>' + '<strong>ANSWER:</strong> ' + tossup_answer + '<br>';
htmlContent += '<br><br><h3 style=\'text-align: center;\'><strong>BONUS</strong></h3>\n<br>' + `${i}) <strong>${question_category}</strong>` + ' ' + `<em>${bonus_format}</em>` + ' ' + bonus_question + '<br><br>' + '<strong>ANSWER:</strong> ' + bonus_answer + '<br><br><hr><br>';
htmlContent = htmlContent.replace(/\n/g, '<br>');
finalizedHTML += htmlContent;
}
const newGeneratedRound = new generatedRound({
htmlContent: finalizedHTML,
requestedBy: interaction.user.id,
authorTag: interaction.user.tag,
timestamp: new Date().toISOString(),
});
newGeneratedRound.save((err, round) => {
if (err) {
console.log(err);
return;
}
interaction.reply(`Here's your round: https://api.adawesome.tech/round/${round._id.toString()}`, { ephemeral: true });
});
});
}
else if (action === 'list') {
let roundsList = await generatedRound.find({ requestedBy: interaction.user.id }).sort({ timestamp: -1 });
let finalMessage = '';
if (!roundsList) {
interaction.reply('You haven\'t requested any roundsList!');
return;
}
if (roundsList.length > 5) {
roundsList = roundsList.slice(0, 5);
}
roundsList.forEach(async (item, index) => {
finalMessage += `${index + 1}. [${item.timestamp.split('T')[0]}](https://api.adawesome.tech/round/${item._id.toString()})\n`;
});
const roundsListEmbed = new Discord.MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.setTitle(`Last 5 roundsList requested by ${interaction.user.tag}`)
.setDescription(finalMessage)
.setTimestamp();
interaction.reply({
embeds: [roundsListEmbed],
ephemeral: true,
});
}
else if (action === 'hit') {
const totalCount = await generatedRound.countDocuments({});
const userCount = await generatedRound.countDocuments({ requestedBy: interaction.user.id });
interaction.reply(`Total Hits: ${totalCount}\nYour Hits: ${userCount}`);
}
}
async function result(interaction) {
if (interaction.channel.id !== '930275699644825600') {
return interaction.reply({ content: 'This command is unavailable outside of the designated channel.', ephemeral: true });
}
const resultEmbed = new Discord.MessageEmbed();
resultEmbed.setTitle(`${interaction.options.get('location').value} Regionals`);
resultEmbed.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() });
resultEmbed.addField('<a:winner:932137838592552960> Winner', `Congratulations to **${interaction.options.get('winner').value}**!`);
if (interaction.options.get('runner-up')) resultEmbed.addField('<:second:932138645601800252> Runners-Up', interaction.options.get('runner-up').value);
if (interaction.options.get('third-place')) resultEmbed.addField('<:third:932138645526315080> Third Place', interaction.options.get('third-place').value);
if (interaction.options.get('honorable-mentions')) resultEmbed.addField('🎖️ Honorable Mentions', interaction.options.get('honorable-mentions').value);
resultEmbed.setColor('#FFFFFF');
resultEmbed.setTimestamp();
await interaction.reply({ embeds: [resultEmbed] });
}
client.on('interactionCreate', async interaction => {
// If the interaction isn't a slash command, return
if (!interaction.isCommand()) return;
switch (interaction.commandName) {
case 'help':
sendHelpMessage(interaction);
break;
case 'train':
training(interaction.options.get('subject') ? interaction.options.get('subject').value : null, interaction);
break;
case 'roundsList':
rounds(interaction.options.getSubcommand(), interaction);
break;
case 'top':
showLeaderboard(interaction);
break;
case 'about':
about(interaction.options.getSubcommand(), interaction);
break;
case 'result':
result(interaction);
}
});
client
.login(process.env.TOKEN)
.then(() => console.log('Running!'))
.catch((error) => console.log(error));

1
package.json

@ -1,5 +1,6 @@
{ {
"dependencies": { "dependencies": {
"@discordjs/rest": "^0.3.0",
"axios": "^0.21.1", "axios": "^0.21.1",
"discord.js": "^13.2.0", "discord.js": "^13.2.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",

137
slashCommands.json

@ -1,137 +0,0 @@
[
{
"name": "train",
"description": "Sends a single training question to be answered",
"options": [
{
"type": 3,
"name": "subject",
"description": "Optional subject to be used as a filter",
"default": false,
"required": false,
"choices": [
{
"name": "astro",
"value": "astro"
},
{
"name": "bio",
"value": "bio"
},
{
"name": "ess",
"value": "ess"
},
{
"name": "chem",
"value": "chem"
},
{
"name": "phys",
"value": "phys"
},
{
"name": "math",
"value": "math"
},
{
"name": "energy",
"value": "energy"
}
]
}
]
},
{
"name": "help",
"description": "Replies with a help message explaining what the bot can do"
},
{
"name": "rounds",
"options": [
{
"type": 1,
"name": "generate",
"description": "Generates a round with randomized questions from https://scibowldb.com/",
"options": []
},
{
"type": 1,
"name": "list",
"description": "Lists your 5 most recently generated rounds with links",
"options": []
},
{
"type": 1,
"name": "hit",
"description": "Shows the total number of rounds hit as well as the number for the specific user",
"options": []
}
],
"description": "Commands regarding rounds generated by AwesomeSciBo"
},
{
"name": "top",
"description": "Lists top ten scores across servers (server specific leaderboard WIP)"
},
{
"name": "about",
"options": [
{
"type": 1,
"name": "contributors",
"description": "Lists contributors to the AwesomeSciBo bot",
"options": []
},
{
"type": 1,
"name": "changelog",
"description": "Lists the 5 most recent changes in a \"git log\" type format",
"options": []
},
{
"type": 1,
"name": "bot",
"description": "Lists information about AwesomeSciBo",
"options": []
}
],
"description": "Commands regarding the creation/development of the bot"
},
{
"name": "result",
"description": "The result of a regional",
"options": [
{
"type": "STRING",
"name": "location",
"description": "The location of the regional",
"required": true
},
{
"type": "STRING",
"name": "winner",
"description": "The advancing team from the regional",
"required": true
},
{
"type": "STRING",
"name": "runner-up",
"description": "The second-place team from the regional",
"required": false
},
{
"type": "STRING",
"name": "third-place",
"description": "The third-place team from the regional",
"required": false
},
{
"type": "STRING",
"name": "honorable-mentions",
"description": "Any extra teams to be mentioned or a space for technicalities to be explained",
"required": false
}
]
}
]

22
yarn.lock

@ -18,6 +18,19 @@
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.4.0.tgz#b6488286a1cc7b41b644d7e6086f25a1c1e6f837" resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.4.0.tgz#b6488286a1cc7b41b644d7e6086f25a1c1e6f837"
integrity sha512-zmjq+l/rV35kE6zRrwe8BHqV78JvIh2ybJeZavBi5NySjWXqN3hmmAKg7kYMMXSeiWtSsMoZ/+MQi0DiQWy2lw== integrity sha512-zmjq+l/rV35kE6zRrwe8BHqV78JvIh2ybJeZavBi5NySjWXqN3hmmAKg7kYMMXSeiWtSsMoZ/+MQi0DiQWy2lw==
"@discordjs/rest@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-0.3.0.tgz#89e06a42b168c91598891d6bf860353e28fba5d2"
integrity sha512-F9aeP3odlAlllM1ciBZLdd+adiAyBj4VaZBejj4UMj4afE2wfCkNTGvYYiRxrXUE9fN7e/BuDP2ePl0tVA2m7Q==
dependencies:
"@discordjs/collection" "^0.4.0"
"@sapphire/async-queue" "^1.1.9"
"@sapphire/snowflake" "^3.0.1"
discord-api-types "^0.26.1"
form-data "^4.0.0"
node-fetch "^2.6.5"
tslib "^2.3.1"
"@eslint/eslintrc@^1.0.5": "@eslint/eslintrc@^1.0.5":
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318"
@ -52,6 +65,11 @@
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.1.9.tgz#ce69611c8753c4affd905a7ef43061c7eb95c01b" resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.1.9.tgz#ce69611c8753c4affd905a7ef43061c7eb95c01b"
integrity sha512-CbXaGwwlEMq+l1TRu01FJCvySJ1CEFKFclHT48nIfNeZXaAAmmwwy7scUKmYHPUa3GhoMp6Qr1B3eAJux6XgOQ== integrity sha512-CbXaGwwlEMq+l1TRu01FJCvySJ1CEFKFclHT48nIfNeZXaAAmmwwy7scUKmYHPUa3GhoMp6Qr1B3eAJux6XgOQ==
"@sapphire/snowflake@^3.0.1":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.0.tgz#fa915798d0a0ce5fa93b16c793185ae5d0a3f117"
integrity sha512-tfHzY+6/5bbHdB+uNqsEQ5rhjaZAoFUrqP/l1S5jwxMdKeSCIiGkJjcE99/WGGdzyWGjTNgNVX/dt4Me/FdMlg==
"@sindresorhus/is@^0.14.0": "@sindresorhus/is@^0.14.0":
version "0.14.0" version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@ -428,7 +446,7 @@ denque@^1.4.1:
resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf"
integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==
discord-api-types@^0.26.0: discord-api-types@^0.26.0, discord-api-types@^0.26.1:
version "0.26.1" version "0.26.1"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.26.1.tgz#726f766ddc37d60da95740991d22cb6ef2ed787b" resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.26.1.tgz#726f766ddc37d60da95740991d22cb6ef2ed787b"
integrity sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ== integrity sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ==
@ -1108,7 +1126,7 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
node-fetch@^2.6.1: node-fetch@^2.6.1, node-fetch@^2.6.5:
version "2.6.7" version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==

Loading…
Cancel
Save