Browse Source

Update line endings

development
Abheek Dhawan 3 years ago
parent
commit
6ac88895c5
Signed by: abheekd GPG Key ID: 7BE81B8C14475B67
  1. 154
      src/commands/about.ts
  2. 28
      src/commands/help.ts
  3. 242
      src/commands/rounds.ts
  4. 80
      src/commands/score.ts
  5. 124
      src/commands/top.ts
  6. 456
      src/commands/train.ts
  7. 94
      src/helpers/db.ts
  8. 3852
      yarn.lock

154
src/commands/about.ts

@ -1,77 +1,77 @@
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, CommandInteraction } from 'discord.js';
import gitlog from 'gitlog';
import userScore from '../models/userScore';
export const 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;
});
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
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.followUp({ 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.followUp({ 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.followUp({ embeds: [aboutBotEmbed] });
}
}
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, CommandInteraction } from 'discord.js';
import gitlog from 'gitlog';
import userScore from '../models/userScore';
export const 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;
});
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
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.followUp({ 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.followUp({ 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.followUp({ embeds: [aboutBotEmbed] });
}
}

28
src/commands/help.ts

@ -1,15 +1,15 @@
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, CommandInteraction } from 'discord.js';
export const data = new SlashCommandBuilder()
.setName('help')
.setDescription('Replies with a help message explaining what the bot can do');
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
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.followUp({ embeds: [helpEmbed] });
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, CommandInteraction } from 'discord.js';
export const data = new SlashCommandBuilder()
.setName('help')
.setDescription('Replies with a help message explaining what the bot can do');
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
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.followUp({ embeds: [helpEmbed] });
}

242
src/commands/rounds.ts

@ -1,122 +1,122 @@
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, CommandInteraction } from 'discord.js';
import axios from 'axios';
import log from '../helpers/log';
import generatedRound from '../models/generatedRound';
export const 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;
});
export async function execute(interaction : CommandInteraction) {
const action = interaction.options.getSubcommand();
switch (action) {
case 'generate': {
interaction.deferReply({ ephemeral: true });
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 : string;
let question_category : string;
let tossup_format : string;
let tossup_answer : string;
let bonus_question : string;
let bonus_format : string;
let bonus_answer : string;
let htmlContent = '';
await axios.post('https://scibowldb.com/api/questions', { categories: ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'] })
.then((response) => {
for (let i = 1; i < 26; i++) {
const questionData = response.data.questions[Math.floor(Math.random() * response.data.questions.length)];
tossup_question = questionData.tossup_question;
tossup_answer = questionData.tossup_answer;
question_category = questionData.category;
tossup_format = questionData.tossup_format;
bonus_question = questionData.bonus_question;
bonus_answer = questionData.bonus_answer;
bonus_format = questionData.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) {
log({ logger: 'rounds', content: `Saving round to DB failed: ${err}`, level: 'error' });
return;
}
interaction.followUp({ content: `Here's your round: https://api.adawesome.tech/round/${round._id.toString()}`, ephemeral: true });
});
});
break;
}
case 'list': {
interaction.deferReply({ ephemeral: true });
let roundsList = await generatedRound.find({ requestedBy: interaction.user.id }).sort({ timestamp: -1 });
let finalMessage = '';
if (!roundsList) {
interaction.followUp('You haven\'t requested any rounds!');
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 rounds requested')
.setDescription(finalMessage)
.setTimestamp();
interaction.followUp({
embeds: [roundsListEmbed],
ephemeral: true,
});
break;
}
case 'hit': {
await interaction.deferReply();
const totalCount = await generatedRound.countDocuments({});
const userCount = await generatedRound.countDocuments({ requestedBy: interaction.user.id });
interaction.followUp(`Total Hits: ${totalCount}\nYour Hits: ${userCount}`);
break;
}
}
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, CommandInteraction } from 'discord.js';
import axios from 'axios';
import log from '../helpers/log';
import generatedRound from '../models/generatedRound';
export const 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;
});
export async function execute(interaction : CommandInteraction) {
const action = interaction.options.getSubcommand();
switch (action) {
case 'generate': {
interaction.deferReply({ ephemeral: true });
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 : string;
let question_category : string;
let tossup_format : string;
let tossup_answer : string;
let bonus_question : string;
let bonus_format : string;
let bonus_answer : string;
let htmlContent = '';
await axios.post('https://scibowldb.com/api/questions', { categories: ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'] })
.then((response) => {
for (let i = 1; i < 26; i++) {
const questionData = response.data.questions[Math.floor(Math.random() * response.data.questions.length)];
tossup_question = questionData.tossup_question;
tossup_answer = questionData.tossup_answer;
question_category = questionData.category;
tossup_format = questionData.tossup_format;
bonus_question = questionData.bonus_question;
bonus_answer = questionData.bonus_answer;
bonus_format = questionData.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) {
log({ logger: 'rounds', content: `Saving round to DB failed: ${err}`, level: 'error' });
return;
}
interaction.followUp({ content: `Here's your round: https://api.adawesome.tech/round/${round._id.toString()}`, ephemeral: true });
});
});
break;
}
case 'list': {
interaction.deferReply({ ephemeral: true });
let roundsList = await generatedRound.find({ requestedBy: interaction.user.id }).sort({ timestamp: -1 });
let finalMessage = '';
if (!roundsList) {
interaction.followUp('You haven\'t requested any rounds!');
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 rounds requested')
.setDescription(finalMessage)
.setTimestamp();
interaction.followUp({
embeds: [roundsListEmbed],
ephemeral: true,
});
break;
}
case 'hit': {
await interaction.deferReply();
const totalCount = await generatedRound.countDocuments({});
const userCount = await generatedRound.countDocuments({ requestedBy: interaction.user.id });
interaction.followUp(`Total Hits: ${totalCount}\nYour Hits: ${userCount}`);
break;
}
}
}

80
src/commands/score.ts

@ -1,40 +1,40 @@
import { SlashCommandBuilder } from '@discordjs/builders';
import { CommandInteraction, MessageEmbed } from 'discord.js';
import log from '../helpers/log';
import userScore from '../models/userScore';
export const data = new SlashCommandBuilder()
.setName('score')
.setDescription('Returns the score of the current user or another')
.addUserOption(option => {
option
.setName('user')
.setDescription('The user to find the score for')
.setRequired(false);
return option;
});
export async function execute(interaction : CommandInteraction) {
const scoreEmbed = new MessageEmbed()
.setColor('#ffffff');
const user = interaction.options.getUser('user') || interaction.user;
userScore.findOne({ authorID: user.id }, async (err, score) => {
if (err) {
log({ logger: 'db', content: `Unable to obtain user: ${err}`, level: 'info' });
}
if (!score) {
await interaction.reply({ content: 'Unfortunately, that user does not seem to have used AwesomeSciBo yet.', ephemeral: true });
return;
}
scoreEmbed
.setAuthor({ name: user.tag, iconURL: user.displayAvatarURL() })
.setDescription(`Score: \`${score.score}\``);
await interaction.reply({ embeds: [scoreEmbed] });
});
}
import { SlashCommandBuilder } from '@discordjs/builders';
import { CommandInteraction, MessageEmbed } from 'discord.js';
import log from '../helpers/log';
import userScore from '../models/userScore';
export const data = new SlashCommandBuilder()
.setName('score')
.setDescription('Returns the score of the current user or another')
.addUserOption(option => {
option
.setName('user')
.setDescription('The user to find the score for')
.setRequired(false);
return option;
});
export async function execute(interaction : CommandInteraction) {
const scoreEmbed = new MessageEmbed()
.setColor('#ffffff');
const user = interaction.options.getUser('user') || interaction.user;
userScore.findOne({ authorID: user.id }, async (err, score) => {
if (err) {
log({ logger: 'db', content: `Unable to obtain user: ${err}`, level: 'info' });
}
if (!score) {
await interaction.reply({ content: 'Unfortunately, that user does not seem to have used AwesomeSciBo yet.', ephemeral: true });
return;
}
scoreEmbed
.setAuthor({ name: user.tag, iconURL: user.displayAvatarURL() })
.setDescription(`Score: \`${score.score}\``);
await interaction.reply({ embeds: [scoreEmbed] });
});
}

124
src/commands/top.ts

@ -1,63 +1,63 @@
import { SlashCommandBuilder } from '@discordjs/builders';
import { CommandInteraction, MessageEmbed } from 'discord.js';
import log from '../helpers/log';
import userScore from '../models/userScore';
export const data = new SlashCommandBuilder()
.setName('top')
.setDescription('Lists top ten scores across servers (server specific leaderboard WIP)');
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
userScore
.find({})
.sort({ score: -1 }) // Sort by descending order
.exec(async (err, obj) => {
if (err) {
log({ logger: 'top', content: `Getting top players failed: ${err}`, level: 'error' });
console.log(err);
}
if (obj.length < 10) {
// Need at least 10 scores for top 10
return interaction.followUp(
`There are only ${obj.length} users, we need at least 10!`,
);
}
const embeds : MessageEmbed[] = [];
let lbMessageContent = '';
for (let i = 0; i < 10; i++) {
lbMessageContent += `${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(lbMessageContent)
.setColor('#ffffff');
embeds.push(leaderboardEmbed);
let sMessageContent = '';
const members = await interaction.guild?.members.fetch();
const serverLeaderBoardArray = await obj.filter(o => members?.some(m => m.user.id === o.authorID));
if (serverLeaderBoardArray.length > 10) {
for (let i = 0; i < 10; i++) {
sMessageContent += `${i + 1}: <@${serverLeaderBoardArray[i].authorID}>: ${serverLeaderBoardArray[i].score}\n`;
}
const sLeaderboardEmbed = new MessageEmbed()
.setTitle(`Top Ten in ${interaction.guild?.name}!`)
.setDescription(sMessageContent)
.setColor('#ffffff');
embeds.push(sLeaderboardEmbed);
}
interaction.followUp({ embeds: embeds });
});
import { SlashCommandBuilder } from '@discordjs/builders';
import { CommandInteraction, MessageEmbed } from 'discord.js';
import log from '../helpers/log';
import userScore from '../models/userScore';
export const data = new SlashCommandBuilder()
.setName('top')
.setDescription('Lists top ten scores across servers (server specific leaderboard WIP)');
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
userScore
.find({})
.sort({ score: -1 }) // Sort by descending order
.exec(async (err, obj) => {
if (err) {
log({ logger: 'top', content: `Getting top players failed: ${err}`, level: 'error' });
console.log(err);
}
if (obj.length < 10) {
// Need at least 10 scores for top 10
return interaction.followUp(
`There are only ${obj.length} users, we need at least 10!`,
);
}
const embeds : MessageEmbed[] = [];
let lbMessageContent = '';
for (let i = 0; i < 10; i++) {
lbMessageContent += `${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(lbMessageContent)
.setColor('#ffffff');
embeds.push(leaderboardEmbed);
let sMessageContent = '';
const members = await interaction.guild?.members.fetch();
const serverLeaderBoardArray = await obj.filter(o => members?.some(m => m.user.id === o.authorID));
if (serverLeaderBoardArray.length > 10) {
for (let i = 0; i < 10; i++) {
sMessageContent += `${i + 1}: <@${serverLeaderBoardArray[i].authorID}>: ${serverLeaderBoardArray[i].score}\n`;
}
const sLeaderboardEmbed = new MessageEmbed()
.setTitle(`Top Ten in ${interaction.guild?.name}!`)
.setDescription(sMessageContent)
.setColor('#ffffff');
embeds.push(sLeaderboardEmbed);
}
interaction.followUp({ embeds: embeds });
});
}

456
src/commands/train.ts

@ -1,228 +1,228 @@
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, MessageActionRow, MessageButton, CommandInteraction, Message } from 'discord.js';
import { decode } from 'html-entities';
import axios from 'axios';
import userScore from '../models/userScore';
import log from '../helpers/log.js';
import { updateScore } from '../helpers/db.js';
export const 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;
});
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
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 {
log({ logger: 'train', content: `Getting user score failed: ${err}`, level: 'error' });
}
});
let categoryArray : string[] = [];
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.followUp({
embeds: [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 questionData = res.data.question;
const tossupQuestion = questionData.tossup_question;
const tossupAnswer = questionData.tossup_answer;
const tossupFormat = questionData.tossup_format;
let answers = tossupAnswer.split(' (ACCEPT: ');
if (answers.length > 1) {
answers[1] = answers[1].slice(0, answers[1].length - 1); // If there are multiple elements, it means there was an 'accept' and therefore a trailing ')' which should be removed
answers = [answers[0], ...answers[1].split(new RegExp(' OR ', 'i'))]; // Use the first element plus the last element split by 'OR' case insensitive
}
interaction.followUp({ content: decode(tossupQuestion), fetchReply: true })
.then(q => {
const questionMessage = q as Message;
const sourceButton = new MessageActionRow()
.addComponents(
new MessageButton()
.setURL(questionData.uri)
.setLabel('Source')
.setStyle('LINK'),
);
switch (tossupFormat) {
case 'Short Answer': {
// eslint-disable-next-line no-case-declarations
const messageFilter = m => m.author.id === interaction.user.id || m.author.id === interaction.client.user?.id;
interaction.channel?.awaitMessages({
filter: messageFilter,
max: 1,
})
.then(collected => {
const answerMsg = collected.first();
if (answerMsg?.author.id === interaction.client.user?.id) return;
let predicted = '';
if (answerMsg?.content.toLowerCase() === tossupAnswer.toLowerCase() || answers.includes(answerMsg?.content.toUpperCase())) {
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 ? answerMsg.author.tag : '', iconURL: answerMsg?.author.displayAvatarURL() })
.addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription('It seems your answer was incorrect. Please react with <:override:955265585086857236> to override your answer if you think you got it right.')
.setColor('#ffffff')
.setTimestamp();
const overrideButton = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId('override')
.setEmoji('<:override:955265585086857236>')
.setStyle('SECONDARY'),
);
answerMsg?.channel.send({
embeds: [overrideEmbed],
components: [overrideButton],
})
.then(overrideMsg => {
const overrideFilter = i => {
return (
['override'].includes(i.customId) &&
i.user.id === answerMsg.author.id
);
};
overrideMsg
.awaitMessageComponent({
filter: overrideFilter,
})
.then(i => {
updateScore(true, score, authorId).then(async msgToReply => {
await i.reply(msgToReply);
overrideMsg.edit({ components: [] });
});
}).catch(err => log({ logger: 'train', content: `Failed to override score: ${err}`, level: 'error' }));
}).catch(err => log({ logger: 'train', content: `Failed to send override message: ${err}`, level: 'error' }));
}
interaction.editReply({ components: [sourceButton] });
}).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' }));
break;
}
case 'Multiple Choice': {
const choices = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId('w')
.setLabel('W')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('x')
.setLabel('X')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('y')
.setLabel('Y')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('z')
.setLabel('Z')
.setStyle('SECONDARY'),
);
interaction.editReply({ components: [choices] });
const mcFilter = i => ['w', 'x', 'y', 'z'].includes(i.customId) && i.user.id === interaction.user.id;
questionMessage.awaitMessageComponent({ filter: mcFilter })
.then(mcChoice => {
if (tossupAnswer.charAt(0).toLowerCase() === mcChoice.customId) {
updateScore(true, score, authorId).then((msgToReply) =>
mcChoice.reply(msgToReply),
);
}
else {
const incorrectEmbed = new MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription(`It seems your answer ${mcChoice.customId.toUpperCase()} was incorrect.`)
.setColor('#ffffff')
.setTimestamp();
mcChoice.reply({ embeds: [incorrectEmbed] });
}
interaction.editReply({ components: [sourceButton] });
});
break;
}
}
}).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' }));
}).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' }));
}
import { SlashCommandBuilder } from '@discordjs/builders';
import { MessageEmbed, MessageActionRow, MessageButton, CommandInteraction, Message } from 'discord.js';
import { decode } from 'html-entities';
import axios from 'axios';
import userScore from '../models/userScore';
import log from '../helpers/log.js';
import { updateScore } from '../helpers/db.js';
export const 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;
});
export async function execute(interaction : CommandInteraction) {
await interaction.deferReply();
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 {
log({ logger: 'train', content: `Getting user score failed: ${err}`, level: 'error' });
}
});
let categoryArray : string[] = [];
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.followUp({
embeds: [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 questionData = res.data.question;
const tossupQuestion = questionData.tossup_question;
const tossupAnswer = questionData.tossup_answer;
const tossupFormat = questionData.tossup_format;
let answers = tossupAnswer.split(' (ACCEPT: ');
if (answers.length > 1) {
answers[1] = answers[1].slice(0, answers[1].length - 1); // If there are multiple elements, it means there was an 'accept' and therefore a trailing ')' which should be removed
answers = [answers[0], ...answers[1].split(new RegExp(' OR ', 'i'))]; // Use the first element plus the last element split by 'OR' case insensitive
}
interaction.followUp({ content: decode(tossupQuestion), fetchReply: true })
.then(q => {
const questionMessage = q as Message;
const sourceButton = new MessageActionRow()
.addComponents(
new MessageButton()
.setURL(questionData.uri)
.setLabel('Source')
.setStyle('LINK'),
);
switch (tossupFormat) {
case 'Short Answer': {
// eslint-disable-next-line no-case-declarations
const messageFilter = m => m.author.id === interaction.user.id || m.author.id === interaction.client.user?.id;
interaction.channel?.awaitMessages({
filter: messageFilter,
max: 1,
})
.then(collected => {
const answerMsg = collected.first();
if (answerMsg?.author.id === interaction.client.user?.id) return;
let predicted = '';
if (answerMsg?.content.toLowerCase() === tossupAnswer.toLowerCase() || answers.includes(answerMsg?.content.toUpperCase())) {
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 ? answerMsg.author.tag : '', iconURL: answerMsg?.author.displayAvatarURL() })
.addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription('It seems your answer was incorrect. Please react with <:override:955265585086857236> to override your answer if you think you got it right.')
.setColor('#ffffff')
.setTimestamp();
const overrideButton = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId('override')
.setEmoji('<:override:955265585086857236>')
.setStyle('SECONDARY'),
);
answerMsg?.channel.send({
embeds: [overrideEmbed],
components: [overrideButton],
})
.then(overrideMsg => {
const overrideFilter = i => {
return (
['override'].includes(i.customId) &&
i.user.id === answerMsg.author.id
);
};
overrideMsg
.awaitMessageComponent({
filter: overrideFilter,
})
.then(i => {
updateScore(true, score, authorId).then(async msgToReply => {
await i.reply(msgToReply);
overrideMsg.edit({ components: [] });
});
}).catch(err => log({ logger: 'train', content: `Failed to override score: ${err}`, level: 'error' }));
}).catch(err => log({ logger: 'train', content: `Failed to send override message: ${err}`, level: 'error' }));
}
interaction.editReply({ components: [sourceButton] });
}).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' }));
break;
}
case 'Multiple Choice': {
const choices = new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId('w')
.setLabel('W')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('x')
.setLabel('X')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('y')
.setLabel('Y')
.setStyle('SECONDARY'),
new MessageButton()
.setCustomId('z')
.setLabel('Z')
.setStyle('SECONDARY'),
);
interaction.editReply({ components: [choices] });
const mcFilter = i => ['w', 'x', 'y', 'z'].includes(i.customId) && i.user.id === interaction.user.id;
questionMessage.awaitMessageComponent({ filter: mcFilter })
.then(mcChoice => {
if (tossupAnswer.charAt(0).toLowerCase() === mcChoice.customId) {
updateScore(true, score, authorId).then((msgToReply) =>
mcChoice.reply(msgToReply),
);
}
else {
const incorrectEmbed = new MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() })
.addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription(`It seems your answer ${mcChoice.customId.toUpperCase()} was incorrect.`)
.setColor('#ffffff')
.setTimestamp();
mcChoice.reply({ embeds: [incorrectEmbed] });
}
interaction.editReply({ components: [sourceButton] });
});
break;
}
}
}).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' }));
}).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' }));
}

94
src/helpers/db.ts

@ -1,47 +1,47 @@
import mongoose from 'mongoose';
import log from '../helpers/log';
import userScore from '../models/userScore';
export async function updateScore(isCorrect : boolean, score : number, authorId : string) {
if (!isCorrect) {
return `Nice try! Your score is still ${score}.`;
}
else {
// TODO: Error handling
const doc = await userScore.findOne({
authorID: authorId,
});
if (!doc) {
const newUserScore = new userScore({
authorID: authorId,
score: score + 4,
});
newUserScore.save(err => {
if (err) {
log({ logger: 'db', content: `Error creating new user ${authorId} for scoring`, level: 'error' });
}
else {
log({ logger: 'db', content: `Successfully created user ${authorId} for scoring`, level: 'debug' });
}
});
}
else {
doc.score = doc.score + 4;
doc.save();
}
return `Great job! Your score is now ${score + 4}.`;
}
}
export async function connect(mongoUri) {
mongoose
.connect(mongoUri, {
useUnifiedTopology: true,
useNewUrlParser: true,
})
.then(() => log({ logger: 'db', content: `Connected to the database at ${mongoUri}!`, level: 'info' }))
.catch(err => log({ logger: 'db', content: `Failed to connect to the database at ${mongoUri}: ${err}`, level: 'fatal' }));
}
import mongoose from 'mongoose';
import log from '../helpers/log';
import userScore from '../models/userScore';
export async function updateScore(isCorrect : boolean, score : number, authorId : string) {
if (!isCorrect) {
return `Nice try! Your score is still ${score}.`;
}
else {
// TODO: Error handling
const doc = await userScore.findOne({
authorID: authorId,
});
if (!doc) {
const newUserScore = new userScore({
authorID: authorId,
score: score + 4,
});
newUserScore.save(err => {
if (err) {
log({ logger: 'db', content: `Error creating new user ${authorId} for scoring`, level: 'error' });
}
else {
log({ logger: 'db', content: `Successfully created user ${authorId} for scoring`, level: 'debug' });
}
});
}
else {
doc.score = doc.score + 4;
doc.save();
}
return `Great job! Your score is now ${score + 4}.`;
}
}
export async function connect(mongoUri) {
mongoose
.connect(mongoUri, {
useUnifiedTopology: true,
useNewUrlParser: true,
})
.then(() => log({ logger: 'db', content: `Connected to the database at ${mongoUri}!`, level: 'info' }))
.catch(err => log({ logger: 'db', content: `Failed to connect to the database at ${mongoUri}: ${err}`, level: 'fatal' }));
}

3852
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save