125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
import {
|
|
CommandInteraction,
|
|
TextChannel,
|
|
SlashCommandBuilder,
|
|
SlashCommandBooleanOption,
|
|
CommandInteractionOptionResolver,
|
|
} from 'discord.js';
|
|
import {writeFileSync} from 'fs';
|
|
|
|
import {Command} from '../../classes/command';
|
|
import {logger} from '../../logger';
|
|
import {config} from '../../config';
|
|
import {getContest, getProblem, getSession} from '../../functions/database';
|
|
|
|
function isTextChannel(data: unknown): data is TextChannel{
|
|
return (data as TextChannel).name !== undefined;
|
|
}
|
|
|
|
type CIOR = CommandInteractionOptionResolver;
|
|
|
|
class Result extends Command{
|
|
get name(){return "result";}
|
|
get description(){return "See the result of a contest.";}
|
|
async execute(interaction: CommandInteraction): Promise<void>{
|
|
if(!isTextChannel(interaction.channel)){
|
|
await interaction.reply({
|
|
content: `Channel name doesn't exist!`
|
|
});
|
|
logger.error(`Channel name doesn't exist`);
|
|
return;
|
|
}
|
|
const contestName = interaction.channel.name;
|
|
const channelId = interaction.channel.id;
|
|
const markdown = (interaction.options as CIOR).getBoolean('markdown') ?? false;
|
|
const contest = await getContest(channelId);
|
|
if(contest === null){
|
|
await interaction.reply({
|
|
content: `The contest in this channel didn't start!`
|
|
});
|
|
logger.error(`Contest ${contestName} didn't start`);
|
|
return;
|
|
}
|
|
let content: string = '';
|
|
content += `# ${contestName}\n\n`
|
|
contest.problems.sort(
|
|
(a, b) => {
|
|
if(a.problemId > b.problemId)
|
|
return 1;
|
|
if(a.problemId < b.problemId)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
);
|
|
const getTime = (time: number) => {
|
|
if(time === -1) return -1;
|
|
return Math.floor((time-contest.startTime)/(1000*60));
|
|
};
|
|
for(const problem of contest.problems){
|
|
const p = await getProblem(problem._id);
|
|
if(p === null) continue;
|
|
content += `## p${p.problemId}\n`;
|
|
if(getTime(p.ac) === -1)
|
|
content += "*Problem unsolved!*\n";
|
|
else
|
|
content += `**AC** at ${getTime(p.ac)} min\n`;
|
|
if(p.wa.length !== 0){
|
|
content += `**WA** at`;
|
|
let isFirst: boolean = true;
|
|
for(const wa of p.wa.sort()){
|
|
if(isFirst) isFirst = false;
|
|
else content += ',';
|
|
content += ` ${getTime(wa)}`;
|
|
}
|
|
content += ' min\n';
|
|
}
|
|
if(p.read.length === 0)
|
|
content += "*Problem unread!*\n";
|
|
else{
|
|
content += `**Read**:\n`;
|
|
for(const session of p.read){
|
|
const s = await getSession(session._id);
|
|
if(s === null) continue;
|
|
content += `- \`${s.name}\` at ${getTime(s.start)} min`;
|
|
if(getTime(s.end) !== -1)
|
|
content += ` (estimate: ${getTime(s.end)} min)`;
|
|
content += '\n';
|
|
}
|
|
}
|
|
if(p.code.length === 0)
|
|
content += "*No one have attempted*\n";
|
|
else{
|
|
content += `**Code**:\n`;
|
|
for(const session of p.code){
|
|
const s = await getSession(session._id);
|
|
if(s === null) continue;
|
|
content += `- \`${s.name}\` at ${getTime(s.start)} min`
|
|
content += ` (estimate: ${getTime(s.end)} min)\n`;
|
|
}
|
|
}
|
|
content += '\n';
|
|
}
|
|
if(markdown){
|
|
const file = `${config.mdBaseDir}/${channelId}.md`;
|
|
logger.log(`Output to ${file}`);
|
|
await writeFileSync(file, content, {encoding: 'utf8'});
|
|
await interaction.reply({files:[file]});
|
|
}else
|
|
await interaction.reply({content: content});
|
|
logger.log(`Command: result of ${contestName}/${channelId}`);
|
|
}
|
|
override build(): SlashCommandBuilder |
|
|
Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">{
|
|
return new SlashCommandBuilder()
|
|
.setName(this.name)
|
|
.setDescription(this.description)
|
|
.addBooleanOption((option: SlashCommandBooleanOption) =>
|
|
option
|
|
.setName('markdown')
|
|
.setDescription('If true then upload markdown file instead.')
|
|
)
|
|
}
|
|
};
|
|
|
|
export const command = new Result();
|