124 lines
5.9 KiB
TypeScript
124 lines
5.9 KiB
TypeScript
import BotCommand from "../../libs/BotCommand";
|
|
import BotClient from "../../libs/BotClient";
|
|
import { ChatInputCommandInteraction, MessageFlags, EmbedBuilder, AttachmentBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, TextChannel } from "discord.js";
|
|
import QuestionScheduler from "../../libs/QuestionScheduler";
|
|
import AnswerGrader from "../../libs/AnswerGrader";
|
|
|
|
const SUBJECTS = ["Mathematics", "Physics", "Chemistry", "Biology", "Computer Science", "Engineering"];
|
|
|
|
export default class WeeklyCommand extends BotCommand {
|
|
constructor() {
|
|
super("weekly", "Answer this week's weekly STEM question", "/weekly");
|
|
this.data.addStringOption(option =>
|
|
option.setName("subject")
|
|
.setDescription("Choose a subject")
|
|
.addChoices(
|
|
{ name: "Mathematics", value: "mathematics" },
|
|
{ name: "Physics", value: "physics" },
|
|
{ name: "Chemistry", value: "chemistry" },
|
|
{ name: "Organic Chemistry", value: "organic chemistry" },
|
|
{ name: "Biology", value: "biology" },
|
|
{ name: "Computer Science", value: "computer science" },
|
|
{ name: "Engineering", value: "engineering" }
|
|
)
|
|
.setRequired(true)
|
|
);
|
|
}
|
|
|
|
override async execute(Discord: any, client: BotClient, interaction: ChatInputCommandInteraction): Promise<any> {
|
|
try {
|
|
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
|
|
|
const subject = interaction.options.getString("subject", true);
|
|
|
|
const scheduler = new QuestionScheduler();
|
|
await scheduler.initialize();
|
|
|
|
const question = await scheduler.getQuestionForPeriod(subject, "weekly");
|
|
|
|
if (!question) {
|
|
return interaction.editReply({ content: `Failed to load this week's ${subject} question. Please try again later.` });
|
|
}
|
|
|
|
const hasAnswered = await scheduler.hasUserAnswered(interaction.user.id, question.id);
|
|
if (hasAnswered) {
|
|
return interaction.editReply({ content: "You've already submitted an answer for this week's question! Check back next Sunday." });
|
|
}
|
|
|
|
const imagePath = await client.typst.renderToImage(question.typst_source);
|
|
|
|
if (!imagePath) {
|
|
// Log error to logs channel
|
|
const logsChannelId = process.env.LOGS_CHANNEL_ID;
|
|
if (logsChannelId) {
|
|
try {
|
|
const logsChannel = await client.channels.fetch(logsChannelId) as TextChannel;
|
|
if (logsChannel?.isTextBased()) {
|
|
const errorEmbed = new EmbedBuilder()
|
|
.setTitle("❌ Typst Render Error - Weekly Question")
|
|
.setColor(0xef4444)
|
|
.addFields(
|
|
{ name: "Subject", value: subject, inline: true },
|
|
{ name: "Question ID", value: question.id, inline: true },
|
|
{ name: "Topic", value: question.topic, inline: false },
|
|
{ name: "User", value: `<@${interaction.user.id}>`, inline: true },
|
|
{ name: "Error", value: "Failed to render Typst image", inline: false }
|
|
)
|
|
.setTimestamp();
|
|
await logsChannel.send({ embeds: [errorEmbed] });
|
|
}
|
|
} catch (logErr) {
|
|
console.error("Failed to send error to logs channel:", logErr);
|
|
}
|
|
}
|
|
return interaction.editReply({
|
|
content: `❌ An error occurred while generating the question image. This has been reported to administrators.`
|
|
});
|
|
}
|
|
|
|
const safeSubject = subject.replace(/\s+/g, '_');
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setTitle(`🎓 Weekly ${subject} Question (PhD Level)`)
|
|
.setDescription(`**Topic:** ${question.topic}\n**Difficulty:** ${question.difficulty_rating}`)
|
|
.setColor(0xfacc15)
|
|
.setImage(`attachment://weekly_${safeSubject}.png`)
|
|
.setFooter({ text: `Resets at 12 AM Sunday` })
|
|
.setTimestamp();
|
|
|
|
const attachment = new AttachmentBuilder(imagePath, { name: `weekly_${safeSubject}.png` });
|
|
|
|
const row = new ActionRowBuilder<ButtonBuilder>()
|
|
.addComponents(
|
|
new ButtonBuilder()
|
|
.setCustomId(`weekly_answer_${question.id}`)
|
|
.setLabel("Submit Answer")
|
|
.setStyle(ButtonStyle.Primary)
|
|
.setEmoji("✍️"),
|
|
new ButtonBuilder()
|
|
.setCustomId(`weekly_report_${question.id}`)
|
|
.setLabel("Report Issue")
|
|
.setStyle(ButtonStyle.Danger)
|
|
.setEmoji("⚠️")
|
|
);
|
|
|
|
await interaction.editReply({
|
|
embeds: [embed],
|
|
files: [attachment],
|
|
components: [row],
|
|
});
|
|
} catch (err) {
|
|
console.error("Error executing weekly command:", err);
|
|
try {
|
|
if (interaction.deferred || interaction.replied) {
|
|
await interaction.editReply({ content: "An error occurred. Please try again later." });
|
|
} else {
|
|
await interaction.reply({ content: "An error occurred. Please try again later.", flags: MessageFlags.Ephemeral });
|
|
}
|
|
} catch (e) {
|
|
console.error("Error sending error reply:", e);
|
|
}
|
|
}
|
|
}
|
|
}
|