import { SlashCommandBuilder } from '@discordjs/builders'; import { MessageEmbed } 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) { 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( 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; 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) + `\n\n||Source: ${questionData.uri}||` }) .then(() => { 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 (questionData.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() || 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, 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(); answerMsg.channel.send({ embeds: [overrideEmbed], }) .then(overrideMsg => { overrideMsg.react('<:override:955265585086857236>'); const filter = (reaction, user) => { return ( ['override'].includes(reaction.emoji.name) && user.id === answerMsg.author.id ); }; overrideMsg .awaitReactions({ filter: filter, max: 1, }) .then(() => { updateScore(true, score, authorId).then((msgToReply) => answerMsg.reply(msgToReply), ); }).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' })); } }).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' })); }).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' })); }).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' })); }